Wikipedia
testwiki
https://test.wikipedia.org/wiki/Main_Page
MediaWiki 1.46.0-wmf.24
first-letter
Media
Special
Talk
User
User talk
Wikipedia
Wikipedia talk
File
File talk
MediaWiki
MediaWiki talk
Template
Template talk
Help
Help talk
Category
Category talk
Thread
Thread talk
Summary
Summary talk
Test namespace 1
Test namespace 1 talk
Test namespace 2
Test namespace 2 talk
Draft
Draft talk
Campaign
Campaign talk
TimedText
TimedText talk
Module
Module talk
SecurePoll
SecurePoll talk
CNBanner
CNBanner talk
Translations
Translations talk
Event
Event talk
Topic
Newsletter
Newsletter talk
Template:No article text
10
78344
739045
738935
2026-04-21T22:49:29Z
Sheffalump
71152
739045
wikitext
text/x-wiki
<div class="mw-parser-output"><!-- don't touch this div to the left, it makes TemplateStyles vroom vroom -->
{{New page DYM}} {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} |{{#ifexist: {{SUBJECTPAGENAME}}|This is the talk page for [[:{{SUBJECTPAGENAME}}]].|[[:{{SUBJECTPAGENAME}}]] has not yet been created. {{Viewing talk page without subject}}|}}}} {{fmbox
| type = system
| id = noarticletext
| image = none
| textstyle = padding: 0.6em 0.9em; <!--Large box needs more padding-->
| text = {{#tag:strong|WARNING:}} {{ #ifeq: {{ #ifeq: {{FULLPAGENAME}} | Template:No article text || }} | <!--Mainspace sister project infobox-->
| {{No article text/sister projects}}
}}{{ #ifeq: {{NAMESPACE}} | {{ns:User talk}}
| {{#tag:strong|No messages}} have been posted for {{BASEPAGENAME}} yet.
| {{#tag:strong|Wikipedia does not contain {{ #switch: {{NAMESPACE}}
| {{ns:0}} = a {{#tag:em|[[{{ns:Project}}:Using pages without namespaces|page]]}}
| {{ns:Talk}} = a {{#tag:em|[[{{ns:Help}}:Using talk pages|discussion page]]}}
| {{TALKSPACE}} = the {{#tag:em|[[{{ns:Help}}:Using talk pages|{{SUBJECTSPACE}}'s discussion page]]}}
| {{ns:Category}} = a {{#tag:em|[[{{ns:Project}}:Categorization|category]]}}
| {{ns:Help}} = a {{#tag:em|[[{{ns:Help}}:Contents|help page]]}}
| {{ns:File}} = a {{#tag:em|[[{{ns:Project}}:Files to upload|file]]}}
| {{ns:Portal}} = a {{#tag:em|[[{{ns:Project}}:Portal|portal]]}}
| {{ns:Template}} = a {{#tag:em|[[{{ns:Project}}:Template messages|template]]}}
| {{ns:User}} = a {{#tag:em|[[{{ns:Project}}:User pages|user page]]}}
| {{ns:Project}} = a {{#tag:em|[[{{ns:Project}}:Project namespace|project page]]}}
| {{ns:MediaWiki}} = a {{#tag:em|[[Special:AllMessages|system message]]}}
| #default = a {{#tag:em|[[{{ns:Project}}:Namespaces|{{NAMESPACE}} page]]}}
}} with this exact name yet.}} {{ #ifeq: {{#invoke:String|replace|source={{PAGENAME}}|}} | {{#invoke:String|replace|source={{SUBPAGENAME}}|}}
| {{ #switch: {{NAMESPACE}}
| {{ns:0}} = Please {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1|name=search for ''{{#invoke:String|replace|source={{PAGENAME}}|}}'' in Test Wikipedia}} to check for alternative titles, names, or spellings.
| {{ns:Talk}} = Before creating this page, please verify that a page called [[{{PAGENAME}}]] exists.
| {{TALKSPACE}} = Before creating this page, please verify that [[:{{SUBJECTPAGENAME}}]] is already made.
| {{ns:Category}} = Please browse the [[Portal:Contents|existing categories]] to check if this category is covered under another name.
| {{ns:Help}} = Please browse the [[{{ns:Help}}:Contents|existing help pages]] to check if this help topic is covered under another name.
| {{ns:File}} = Please do not manually create this page. If you want to upload ''File:{{PAGENAME}}'', Please see the [[{{ns:Project}}:Files to upload|Files to upload]] for instructions & tutorials.
| {{ns:Portal}} = Please browse the [[{{ns:Portal}}:Contents/Portals|existing portals]] to check for similar topics.
| {{ns:Template}} = Please browse the [[{{ns:Project}}:Template messages|existing templates]] to check if this standardized message is available under another name.
| {{ns:User}} = In general, this page should be created and edited by [[User:{{PAGENAME}}]]. If you're already registered, Please [[Special:UserLogin|log in]]. If you don't have an account, [[Special:CreateAccount|create one]]. If you are {{PAGENAME}}, click [[Special:Edit/User:{{PAGENAME}}|create this page]]. If you are not {{PAGENAME}}, don't click "create this page".
| {{ns:Project}} = Please browse the [[{{ns:Project}}:List of policies and guidelines|existing policies and guidelines]] or [[Special:Search/{{ns:Project}}:{{PAGENAME}}|search]] for similar existing project pages.
| {{ns:MediaWiki}} = Please browse the [[Special:AllMessages|existing system messages]] to check if this Non-CSS, Non-JavaScript, Non-JSON, CSS, JavaScript, & JSON pages are available under another name.
| #default = Please browse the [[{{ns:Project}}:Namespaces|existing {{NAMESPACE}} pages]] to check if this {{NAMESPACE}} namespace is available under another name.
}}
| {{ #ifeq: {{{nopermission}}} | yes |You do not have permission to edit pages in the {{#if: {{NAMESPACE}} | '''[[Wikipedia:Namespaces|{{NAMESPACE}}]]''' namespace | '''[[Wikipedia:Namespaces|Page]]''' namespace }}. Please [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in] or [{{fullurl:Special:UserLogin/signup|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} sign up] if you want to create this page. (This page is currently protected so only administrators, system administrators, template editors, extended confirmed users, page movers, autoconfirmed users, & logged in users can make it, '''not''' temporary accounts or unregistered (IP) users). (If you're logged out, {{SITENAME}} has restricted the ability to create new pages. You can go back and edit an existing page, or [[Special:UserLogin|log in or create an account]]. If you're logged in, You do not have permission to create new pages.) Please see [[{{ns:Project}}:Subpages|Subpages]] for subpages if you do not create it because this isn't needed. To protect the page, click [[Special:Protect/{{FULLPAGENAME}}|protect]].| Before creating this page in the {{#if: {{NAMESPACE}} | '''[[Wikipedia:Namespaces|{{NAMESPACE}}]]''' namespace | '''[[Wikipedia:Namespaces|Page]]''' namespace }}, please see [[{{ns:Project}}:Subpages|Subpages]] for subpages. To protect the page, click [[Special:Protect/{{FULLPAGENAME}}|protect]].}}
}}
}}{{ #ifeq: {{{nopermission}}} | yes
|* {{ #switch: {{#invoke:Effective protection level|create|{{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}
| sysop = Administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[Wikipedia:Administrators|administrators]] to create new pages, system messages, & JSON pages.
| interfaceadmin = System administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will </span>be [[Wikipedia:System administrators|system administrators]] to create new CSS & JavaScript pages.
| templateeditor = Template editors & administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[WP:Template editor|template editors]] to create new pages.
| extendedconfirmed = Extended confirmed users & administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[Wikipedia:User access levels#Extendedconfirmed|extended confirmed users]] to create new pages.
| autoconfirmed = Autoconfirmed users can't edit this {{NAMESPACE}} page, but still be able to edit pages by users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will </span>be [[WP:User access levels#Autoconfirmed users|autoconfirmed users]] to create new pages. (After 4 days & 10 edits on English Wikipedia.)
| user = Some users can't edit this {{NAMESPACE}} page, but still be able to edit pages by anonymous users & temporally accounts. You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] to make this page. {{#ifeq:{{NAMESPACE}}|{{ns:Category}}|Or, you can [[Wikipedia:Articles for creation/Categories|request the creation of a new category]].}}
| #default = Users with the "Create" permission can't edit this {{NAMESPACE}} page. (You do not have permission to create this page when not logged in). You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will be [[WP:User access levels#Autoconfirmed users|autoconfirmed users]] to make your own page.
}}
|{{#ifeq:{{#invoke:Title blacklist|main|action=create|pagename={{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}|templateeditor|* Administrators, template editors, & page movers can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will be [[Wikipedia:Administrators|administrators]], [[Wikipedia:Template editors|template editors]], & [[Wikipedia:Page movers|page movers]] to create new ones.|* ''' {{ #switch: {{NAMESPACE}}
| {{ns:3}} = {{#ifeq:{{ROOTPAGENAME}}|{{PAGENAME}}|[[Special:Edit/{{FULLPAGENAME}}|Post a message to ''{{#invoke:String|replace|source={{PAGENAME}}|}}]]''|[[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|leave a message about the ''{{#invoke:String|replace|source={{SUBJECTPAGENAME}}|}}]]''}}
| {{ns:5}}
| {{ns:7}}
| {{ns:9}}
| {{ns:11}}
| {{ns:13}}
| {{ns:15}}
| {{ns:101}}
| {{ns:119}}
| {{ns:711}}
| {{ns:829}}
| {{ns:1}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|leave a message about the ''{{#invoke:String|replace|source={{SUBJECTPAGENAME}}|}}]]''
| {{ns:Category}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'']]
| {{ns:Project}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|start a discussion about the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}]]''
| #default = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]]
}}''' {{ #if: {{NAMESPACE}}
|by using the '''"[[Wikipedia:Namespaces|{{NAMESPACE}}]]"''' namespace|by using the '''"[[Wikipedia:Namespaces|Page]]"''' namespace, using the [[Wikipedia:Article wizard|Article Wizard]] if you want to create it & submit it for review, or [[{{ns:Project}}:Requested articles|add a request for the new page]]
}}.}}
}}{{ #switch: {{NAMESPACE}}
| {{ns:0}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing pages.
* [[wiktionary:en:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wiktionary, our sister dictionary project.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:User}} =
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this user page]].
| {{ns:User talk}} =
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this user talk page]].
| {{ns:Category}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing categories.
* [[wiktionary:en:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wiktionary, our sister dictionary project.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:File}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing files.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:Template}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing templates.
* [[wikimedia:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wikimedia, the Free Templates, Categories, & More!
| #default =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing {{#if: {{NAMESPACE}} | pages with the '''"[[{{ns:Project}}:Namespaces|{{NAMESPACE}}]]"''' namespace | pages without using [[{{ns:Project}}:Namespaces|namespaces]] }}.
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this title]].
}}
* {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} | You can go back to your [[:{{SUBJECTPAGENAME}}|subject page]] | Please discuss changes on your [[{{TALKPAGENAME}}|talk page]] }}.
<div id="noarticletext_technical">
----
'''Other reasons that may be displayed this message:'''
* If {{ #ifeq: {{NAMESPACE}} | {{ns:File}} | a file | a page }} was recently edited here, it may be invisible. (Because of a delay in updating the database.) You can wait a few minutes to complete, you can [[Special:Purge/{{FULLPAGENAME}}|click it]] for purging, or you may want to [[Special:Protect/{{FULLPAGENAME}}|protect]] this page. You can also want to [[Special:Edit/{{FULLPAGENAME}}|edit this page]] or [[Special:Delete/{{FULLPAGENAME}}|delete this page]].
* Titles on the Test Wikipedia is in the '''[[w:en:Case sensitivity|case sensitivity]]''' except for this first character; please {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1|name=check the Test Wikipedia's alternative capitalizations}} and consider adding some [[Wikipedia:Redirects|redirects]] here to the correct title. If the page you are looking for is not TestSite, see its corresponding [[w:en:Main Page|language Wikipedia]]. If the page is not a [[w:en:test wiki|test wiki]], check one of the [[wikimedia:Main Page|other Foundation wikis]].
* If this page has been deleted, try [{{fullurl:Special:Log/delete|page={{#invoke:String|replace|source={{FULLPAGENAMEE}}|}}}} checking the '''deletion log'''], and see [[Wikipedia:Why was the page I created deleted?|the Why was the page I created deleted?]] for possible reasons.
</div>
}}
<!--End fmbox --><!--Start book box-->{{#if:{{#invoke:String|match|{{FULLPAGENAME}}|^Book:|nomatch= }}{{#invoke:String|match|{{FULLPAGENAME}}|^Book talk:|nomatch= }}|{{fmbox|type=system|text=<strong>This page may have existed as part of the former book & talk namespace.</strong> If so it was likely moved to [[{{#invoke:String|replace|{{#invoke:String|replace|{{FULLPAGENAME}}|^Book talk:|Book:|plain=False}}|^Book:|Wikipedia:Books/archive/|plain=False}}]] before being deleted. See [[Wikipedia:Books]] for more information.}}}}<!--End book box-->
</div>
== {{ #switch: {{NAMESPACE}}
| {{ns:0}} = Page
| {{ns:Talk}} = [[{{ns:Help}}:Using talk pages|Discussion page]]
| {{TALKSPACE}} = [[{{ns:Help}}:Using talk pages|{{SUBJECTSPACE}}'s Discussion page]]
| {{ns:Category}} = [[{{ns:Project}}:Categorization|Category]]
| {{ns:Help}} = [[{{ns:Help}}:Contents|Help page]]
| {{ns:File}} = [[{{ns:Project}}:Files to upload|File]]
| {{ns:Portal}} = [[{{ns:Project}}:Portal|Portal]]
| {{ns:Template}} = [[{{ns:Project}}:Template messages|Template]]
| {{ns:User}} = [[{{ns:Project}}:User pages|User page]]
| {{ns:Project}} = [[{{ns:Project}}:Project namespace|Project page]]
| {{ns:MediaWiki}} = [[Special:AllMessages|System message]]
| #default = {{NAMESPACE}} page}} not showing up? ==
Reload the page you just created "'''{{FULLPAGENAME}}'''."
<small>You may also [[Wikipedia:Administrators' noticeboard|contact an administrator]] before recreating this page.</small>
If you can't find the page, you can [[Special:Search/{{FULLPAGENAME}}|search for an existing page]] or [[Special:Edit/{{FULLPAGENAME}}|create it]].
{{ #ifeq: {{{nopermission}}} | yes |{{fmbox
| type = warning
| id = noarticletext
| image = none
| text = You do not have permission to create this page. It may have been restricted from creation.}}|{{fmbox
| type = system
| id = noarticletext
| image = none
| text = If you can't find {{FULLPAGENAME}}, you can also include the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|veaction=edit}} visual editor]</span>, {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} | the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit&veswitched=1}} source editor]</span>, or [[Special:NewSection/{{FULLPAGENAME}}|create a new section]] | or the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit&veswitched=1}} source editor]</span> }} for editing.}}}}
<noinclude>
{{documentation}}
</noinclude>
elm7td7usxzbp1xurwqbmaco53q3uuo
739046
739045
2026-04-21T22:55:42Z
Sheffalump
71152
739046
wikitext
text/x-wiki
<div class="mw-parser-output"><!-- don't touch this div to the left, it makes TemplateStyles vroom vroom -->
{{New page DYM}} {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} |{{#ifexist: {{SUBJECTPAGENAME}}|This is the talk page for [[:{{SUBJECTPAGENAME}}]].|[[:{{SUBJECTPAGENAME}}]] has not yet been created. {{Viewing talk page without subject}}|}}}} {{fmbox
| type = system
| id = noarticletext
| image = none
| textstyle = padding: 0.6em 0.9em; <!--Large box needs more padding-->
| text = {{#tag:strong|WARNING:}} {{ #ifeq: {{ #ifeq: {{FULLPAGENAME}} | Template:No article text || }} | <!--Mainspace sister project infobox-->
| {{No article text/sister projects}}
}}{{ #ifeq: {{NAMESPACE}} | {{ns:User talk}}
| {{#tag:strong|No messages}} have been posted for {{BASEPAGENAME}} yet.
| {{#tag:strong|Wikipedia does not contain {{ #switch: {{NAMESPACE}}
| {{ns:0}} = a {{#tag:em|[[{{ns:Project}}:Using pages without namespaces|page]]}}
| {{ns:Talk}} = a {{#tag:em|[[{{ns:Help}}:Using talk pages|discussion page]]}}
| {{TALKSPACE}} = the {{#tag:em|[[{{ns:Help}}:Using talk pages|{{SUBJECTSPACE}}'s discussion page]]}}
| {{ns:Category}} = a {{#tag:em|[[{{ns:Project}}:Categorization|category]]}}
| {{ns:Help}} = a {{#tag:em|[[{{ns:Help}}:Contents|help page]]}}
| {{ns:File}} = a {{#tag:em|[[{{ns:Project}}:Files to upload|file]]}}
| {{ns:Portal}} = a {{#tag:em|[[{{ns:Project}}:Portal|portal]]}}
| {{ns:Template}} = a {{#tag:em|[[{{ns:Project}}:Template messages|template]]}}
| {{ns:User}} = a {{#tag:em|[[{{ns:Project}}:User pages|user page]]}}
| {{ns:Project}} = a {{#tag:em|[[{{ns:Project}}:Project namespace|project page]]}}
| {{ns:MediaWiki}} = a {{#tag:em|[[Special:AllMessages|system message]]}}
| #default = a {{#tag:em|[[{{ns:Project}}:Namespaces|{{NAMESPACE}} page]]}}
}} with this exact name yet.}} {{ #ifeq: {{#invoke:String|replace|source={{PAGENAME}}|}} | {{#invoke:String|replace|source={{SUBPAGENAME}}|}}
| {{ #switch: {{NAMESPACE}}
| {{ns:0}} = Please {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1|name=search for ''{{#invoke:String|replace|source={{PAGENAME}}|}}'' in Test Wikipedia}} to check for alternative titles, names, or spellings.
| {{ns:Talk}} = Before creating this page, please verify that a page called [[{{PAGENAME}}]] exists.
| {{TALKSPACE}} = Before creating this page, please verify that [[:{{SUBJECTPAGENAME}}]] is already made.
| {{ns:Category}} = Please browse the [[Portal:Contents|existing categories]] to check if this category is covered under another name.
| {{ns:Help}} = Please browse the [[{{ns:Help}}:Contents|existing help pages]] to check if this help topic is covered under another name.
| {{ns:File}} = Please do not manually create this page. If you want to upload ''File:{{PAGENAME}}'', Please see the [[{{ns:Project}}:Files to upload|Files to upload]] for instructions & tutorials.
| {{ns:Portal}} = Please browse the [[{{ns:Portal}}:Contents/Portals|existing portals]] to check for similar topics.
| {{ns:Template}} = Please browse the [[{{ns:Project}}:Template messages|existing templates]] to check if this standardized message is available under another name.
| {{ns:User}} = In general, this page should be created and edited by [[User:{{PAGENAME}}]]. If you're already registered, Please [[Special:UserLogin|log in]]. If you don't have an account, [[Special:CreateAccount|create one]]. If you are {{PAGENAME}}, click [[Special:Edit/User:{{PAGENAME}}|create this page]]. If you are not {{PAGENAME}}, don't click "create this page".
| {{ns:Project}} = Please browse the [[{{ns:Project}}:List of policies and guidelines|existing policies and guidelines]] or [[Special:Search/{{ns:Project}}:{{PAGENAME}}|search]] for similar existing project pages.
| {{ns:MediaWiki}} = Please browse the [[Special:AllMessages|existing system messages]] to check if this Non-CSS, Non-JavaScript, Non-JSON, CSS, JavaScript, & JSON pages are available under another name.
| #default = Please browse the [[{{ns:Project}}:Namespaces|existing {{NAMESPACE}} pages]] to check if this {{NAMESPACE}} namespace is available under another name.
}}
| {{ #ifeq: {{{nopermission}}} | yes |You do not have permission to edit pages in the {{#if: {{NAMESPACE}} | '''[[Wikipedia:Namespaces|{{NAMESPACE}}]]''' namespace | '''[[Wikipedia:Namespaces|Page]]''' namespace }}. Please [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in] or [{{fullurl:Special:UserLogin/signup|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} sign up] if you want to create this page. (This page is currently protected so only administrators, system administrators, template editors, extended confirmed users, page movers, autoconfirmed users, & logged in users can make it, '''not''' temporary accounts or unregistered (IP) users). (If you're logged out, {{SITENAME}} has restricted the ability to create new pages. You can go back and edit an existing page, or [[Special:UserLogin|log in or create an account]]. If you're logged in, You do not have permission to create new pages.) Please see [[{{ns:Project}}:Subpages|Subpages]] for subpages if you do not create it because this isn't needed. To protect the page, click [[Special:Protect/{{FULLPAGENAME}}|protect]].| Before creating this page in the {{#if: {{NAMESPACE}} | '''[[Wikipedia:Namespaces|{{NAMESPACE}}]]''' namespace | '''[[Wikipedia:Namespaces|Page]]''' namespace }}, please see [[{{ns:Project}}:Subpages|Subpages]] for subpages. To protect the page, click [[Special:Protect/{{FULLPAGENAME}}|protect]].}}
}}
}}{{ #ifeq: {{{nopermission}}} | yes
|* {{ #switch: {{#invoke:Effective protection level|create|{{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}
| sysop = Administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[Wikipedia:Administrators|administrators]] to create new pages, system messages, & JSON pages.
| interfaceadmin = System administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will </span>be [[Wikipedia:System administrators|system administrators]] to create new CSS & JavaScript pages.
| templateeditor = Template editors & administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[WP:Template editor|template editors]] to create new pages.
| extendedconfirmed = Extended confirmed users & administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[Wikipedia:User access levels#Extendedconfirmed|extended confirmed users]] to create new pages.
| autoconfirmed = Autoconfirmed users can't edit this {{NAMESPACE}} page, but still be able to edit pages by users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will </span>be [[WP:User access levels#Autoconfirmed users|autoconfirmed users]] to create new pages. (After 4 days & 10 edits on English Wikipedia.)
| user = Some users can't edit this {{NAMESPACE}} page, but still be able to edit pages by anonymous users & temporally accounts. You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] to make this page. {{#ifeq:{{NAMESPACE}}|{{ns:Category}}|Or, you can [[Wikipedia:Articles for creation/Categories|request the creation of a new category]].}}
| #default = Users with the "Create" permission can't edit this {{NAMESPACE}} page. (You do not have permission to create this page when not logged in). You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will be [[WP:User access levels#Autoconfirmed users|autoconfirmed users]] to make your own page.
}}
|{{#ifeq:{{#invoke:Title blacklist|main|action=create|pagename={{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}|templateeditor|* Administrators, template editors, & page movers can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will be [[Wikipedia:Administrators|administrators]], [[Wikipedia:Template editors|template editors]], & [[Wikipedia:Page movers|page movers]] to create new ones.|* ''' {{ #switch: {{NAMESPACE}}
| {{ns:3}} = {{#ifeq:{{ROOTPAGENAME}}|{{PAGENAME}}|[[Special:Edit/{{FULLPAGENAME}}|Post a message to ''{{#invoke:String|replace|source={{PAGENAME}}|}}]]''|[[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|leave a message about the ''{{#invoke:String|replace|source={{SUBJECTPAGENAME}}|}}]]''}}
| {{ns:5}}
| {{ns:7}}
| {{ns:9}}
| {{ns:11}}
| {{ns:13}}
| {{ns:15}}
| {{ns:101}}
| {{ns:119}}
| {{ns:711}}
| {{ns:829}}
| {{ns:1}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|leave a message about the ''{{#invoke:String|replace|source={{SUBJECTPAGENAME}}|}}]]''
| {{ns:Category}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'']]
| {{ns:Project}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|start a discussion about the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}]]''
| #default = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]]
}}''' {{ #if: {{NAMESPACE}}
|by using the '''"[[Wikipedia:Namespaces|{{NAMESPACE}}]]"''' namespace|by using the '''"[[Wikipedia:Namespaces|Page]]"''' namespace, using the [[Wikipedia:Article wizard|Article Wizard]] if you want to create it & submit it for review, or [[{{ns:Project}}:Requested articles|add a request for the new page]]
}}.}}
}}{{ #switch: {{NAMESPACE}}
| {{ns:0}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing pages.
* [[wiktionary:en:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wiktionary, our sister dictionary project.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:User}} =
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this user page]].
| {{ns:User talk}} =
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this user talk page]].
| {{ns:Category}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing categories.
* [[wiktionary:en:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wiktionary, our sister dictionary project.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:File}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing files.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:Template}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing templates.
* [[wikimedia:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wikimedia, the Free Templates, Categories, & More!
| #default =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing {{#if: {{NAMESPACE}} | pages with the '''"[[{{ns:Project}}:Namespaces|{{NAMESPACE}}]]"''' namespace | pages without using [[{{ns:Project}}:Namespaces|namespaces]] }}.
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this title]].
}}
* {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} | You can go back to your [[:{{SUBJECTPAGENAME}}|subject page]] | Please discuss changes on your [[{{TALKPAGENAME}}|talk page]] }}.
<div id="noarticletext_technical">
----
'''Other reasons that may be displayed this message:'''
* If {{ #ifeq: {{NAMESPACE}} | {{ns:File}} | a file | a page }} was recently edited here, it may be invisible. (Because of a delay in updating the database.) You can wait a few minutes to complete, you can [[Special:Purge/{{FULLPAGENAME}}|click it]] for purging, or you may want to [[Special:Protect/{{FULLPAGENAME}}|protect]] this page. You can also want to [[Special:Edit/{{FULLPAGENAME}}|edit this page]] or [[Special:Delete/{{FULLPAGENAME}}|delete this page]].
* Titles on the Test Wikipedia is in the '''[[w:en:Case sensitivity|case sensitivity]]''' except for this first character; please {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1|name=check the Test Wikipedia's alternative capitalizations}} and consider adding some [[Wikipedia:Redirects|redirects]] here to the correct title. If the page you are looking for is not TestSite, see its corresponding [[w:en:Main Page|language Wikipedia]]. If the page is not a [[w:en:test wiki|test wiki]], check one of the [[wikimedia:Main Page|other Foundation wikis]].
* If this page has been deleted, try [{{fullurl:Special:Log/delete|page={{#invoke:String|replace|source={{FULLPAGENAMEE}}|}}}} checking the '''deletion log'''], and see [[Wikipedia:Why was the page I created deleted?|the Why was the page I created deleted?]] for possible reasons.
</div>
}}
<!--End fmbox --><!--Start book box-->{{#if:{{#invoke:String|match|{{FULLPAGENAME}}|^Book:|nomatch= }}{{#invoke:String|match|{{FULLPAGENAME}}|^Book talk:|nomatch= }}|{{fmbox|type=system|text=<strong>This page may have existed as part of the former book & talk namespace.</strong> If so it was likely moved to [[{{#invoke:String|replace|{{#invoke:String|replace|{{FULLPAGENAME}}|^Book talk:|Book:|plain=False}}|^Book:|Wikipedia:Books/archive/|plain=False}}]] before being deleted. See [[Wikipedia:Books]] for more information.}}}}<!--End book box-->
</div>
== {{ #switch: {{NAMESPACE}}
| {{ns:0}} = Page
| {{ns:Talk}} = [[{{ns:Help}}:Using talk pages|Discussion page]]
| {{TALKSPACE}} = [[{{ns:Help}}:Using talk pages|{{SUBJECTSPACE}}'s Discussion page]]
| {{ns:Category}} = [[{{ns:Project}}:Categorization|Category]]
| {{ns:Help}} = [[{{ns:Help}}:Contents|Help page]]
| {{ns:File}} = [[{{ns:Project}}:Files to upload|File]]
| {{ns:Portal}} = [[{{ns:Project}}:Portal|Portal]]
| {{ns:Template}} = [[{{ns:Project}}:Template messages|Template]]
| {{ns:User}} = [[{{ns:Project}}:User pages|User page]]
| {{ns:Project}} = [[{{ns:Project}}:Project namespace|Project page]]
| {{ns:MediaWiki}} = [[Special:AllMessages|System message]]
| #default = {{NAMESPACE}} page}} not showing up? ==
Reload the page you just created "'''{{FULLPAGENAME}}'''."
<small>You may also [[Wikipedia:Administrators' noticeboard|contact an administrator]] before recreating this page.</small>
If you can't find the page, you can [[Special:Search/{{FULLPAGENAME}}|search for an existing page]] or [[Special:Edit/{{FULLPAGENAME}}|create it]].
{{ #ifeq: {{{nopermission}}} | yes |{{fmbox
| type = warning
| id = noarticletext
| image = none
| text = You do not have permission to create this page. It may have been {{#if {{PROTECTIONLEVEL:create}} | protected | restricted }} from creation. <span class="user-show">If I think this page should be created here, please unprotect this page at [[Wikipedia:Administrators' noticeboard|the Administrators' noticeboard]].</span>.}}|{{fmbox
| type = system
| id = noarticletext
| image = none
| text = If you can't find {{FULLPAGENAME}}, you can also include the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|veaction=edit}} visual editor]</span>, {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} | the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit&veswitched=1}} source editor]</span>, or [[Special:NewSection/{{FULLPAGENAME}}|create a new section]] | or the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit&veswitched=1}} source editor]</span> }} for editing.}}}}
<noinclude>
{{documentation}}
</noinclude>
owe9zf895ydvai4oirdergbsitgqrdr
739054
739046
2026-04-22T00:15:21Z
Sheffalump
71152
739054
wikitext
text/x-wiki
<div class="mw-parser-output"><!-- don't touch this div to the left, it makes TemplateStyles vroom vroom -->
{{New page DYM}} {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} |{{#ifexist: {{SUBJECTPAGENAME}}|This is the talk page for [[:{{SUBJECTPAGENAME}}]].|[[:{{SUBJECTPAGENAME}}]] has not yet been created. {{Viewing talk page without subject}}|}}}} {{fmbox
| type = system
| id = noarticletext
| image = none
| textstyle = padding: 0.6em 0.9em; <!--Large box needs more padding-->
| text = {{#tag:strong|WARNING:}} {{ #ifeq: {{ #ifeq: {{FULLPAGENAME}} | Template:No article text || }} | <!--Mainspace sister project infobox-->
| {{No article text/sister projects}}
}}{{ #ifeq: {{NAMESPACE}} | {{ns:User talk}}
| {{#tag:strong|No messages}} have been posted for {{BASEPAGENAME}} yet.
| {{#tag:strong|Wikipedia does not contain {{ #switch: {{NAMESPACE}}
| {{ns:0}} = a {{#tag:em|[[{{ns:Project}}:Using pages without namespaces|page]]}}
| {{ns:Talk}} = a {{#tag:em|[[{{ns:Help}}:Using talk pages|discussion page]]}}
| {{TALKSPACE}} = the {{#tag:em|[[{{ns:Help}}:Using talk pages|{{SUBJECTSPACE}}'s discussion page]]}}
| {{ns:Category}} = a {{#tag:em|[[{{ns:Project}}:Categorization|category]]}}
| {{ns:Help}} = a {{#tag:em|[[{{ns:Help}}:Contents|help page]]}}
| {{ns:File}} = a {{#tag:em|[[{{ns:Project}}:Files to upload|file]]}}
| {{ns:Portal}} = a {{#tag:em|[[{{ns:Project}}:Portal|portal]]}}
| {{ns:Template}} = a {{#tag:em|[[{{ns:Project}}:Template messages|template]]}}
| {{ns:User}} = a {{#tag:em|[[{{ns:Project}}:User pages|user page]]}}
| {{ns:Project}} = a {{#tag:em|[[{{ns:Project}}:Project namespace|project page]]}}
| {{ns:MediaWiki}} = a {{#tag:em|[[Special:AllMessages|system message]]}}
| #default = a {{#tag:em|[[{{ns:Project}}:Namespaces|{{NAMESPACE}} page]]}}
}} with this exact name yet.}} {{ #ifeq: {{#invoke:String|replace|source={{PAGENAME}}|}} | {{#invoke:String|replace|source={{SUBPAGENAME}}|}}
| {{ #switch: {{NAMESPACE}}
| {{ns:0}} = Please {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1|name=search for ''{{#invoke:String|replace|source={{PAGENAME}}|}}'' in Test Wikipedia}} to check for alternative titles, names, or spellings.
| {{ns:Talk}} = Before creating this page, please verify that a page called [[{{PAGENAME}}]] exists.
| {{TALKSPACE}} = Before creating this page, please verify that [[:{{SUBJECTPAGENAME}}]] is already made.
| {{ns:Category}} = Please browse the [[Portal:Contents|existing categories]] to check if this category is covered under another name.
| {{ns:Help}} = Please browse the [[{{ns:Help}}:Contents|existing help pages]] to check if this help topic is covered under another name.
| {{ns:File}} = Please do not manually create this page. If you want to upload ''File:{{PAGENAME}}'', Please see the [[{{ns:Project}}:Files to upload|Files to upload]] for instructions & tutorials.
| {{ns:Portal}} = Please browse the [[{{ns:Portal}}:Contents/Portals|existing portals]] to check for similar topics.
| {{ns:Template}} = Please browse the [[{{ns:Project}}:Template messages|existing templates]] to check if this standardized message is available under another name.
| {{ns:User}} = In general, this page should be created and edited by [[User:{{PAGENAME}}]]. If you're already registered, Please [[Special:UserLogin|log in]]. If you don't have an account, [[Special:CreateAccount|create one]]. If you are {{PAGENAME}}, click [[Special:Edit/User:{{PAGENAME}}|create this page]]. If you are not {{PAGENAME}}, don't click "create this page".
| {{ns:Project}} = Please browse the [[{{ns:Project}}:List of policies and guidelines|existing policies and guidelines]] or [[Special:Search/{{ns:Project}}:{{PAGENAME}}|search]] for similar existing project pages.
| {{ns:MediaWiki}} = Please browse the [[Special:AllMessages|existing system messages]] to check if this Non-CSS, Non-JavaScript, Non-JSON, CSS, JavaScript, & JSON pages are available under another name.
| #default = Please browse the [[{{ns:Project}}:Namespaces|existing {{NAMESPACE}} pages]] to check if this {{NAMESPACE}} namespace is available under another name.
}}
| {{ #ifeq: {{{nopermission}}} | yes |You do not have permission to edit pages in the {{#if: {{NAMESPACE}} | '''[[Wikipedia:Namespaces|{{NAMESPACE}}]]''' namespace | '''[[Wikipedia:Namespaces|Page]]''' namespace }}. Please [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in] or [{{fullurl:Special:UserLogin/signup|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} sign up] if you want to create this page. (This page is currently protected so only administrators, system administrators, template editors, extended confirmed users, page movers, autoconfirmed users, & logged in users can make it, '''not''' temporary accounts or unregistered (IP) users). (If you're logged out, {{SITENAME}} has restricted the ability to create new pages. You can go back and edit an existing page, or [[Special:UserLogin|log in or create an account]]. If you're logged in, You do not have permission to create new pages.) Please see [[{{ns:Project}}:Subpages|Subpages]] for subpages if you do not create it because this isn't needed. To protect the page, click [[Special:Protect/{{FULLPAGENAME}}|protect]].| Before creating this page in the {{#if: {{NAMESPACE}} | '''[[Wikipedia:Namespaces|{{NAMESPACE}}]]''' namespace | '''[[Wikipedia:Namespaces|Page]]''' namespace }}, please see [[{{ns:Project}}:Subpages|Subpages]] for subpages. To protect the page, click [[Special:Protect/{{FULLPAGENAME}}|protect]].}}
}}
}}{{ #ifeq: {{{nopermission}}} | yes
|* {{ #switch: {{#invoke:Effective protection level|create|{{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}
| sysop = Administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[Wikipedia:Administrators|administrators]] to create new pages, system messages, & JSON pages.
| interfaceadmin = System administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will </span>be [[Wikipedia:System administrators|system administrators]] to create new CSS & JavaScript pages.
| templateeditor = Template editors & administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[WP:Template editor|template editors]] to create new pages.
| extendedconfirmed = Extended confirmed users & administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[Wikipedia:User access levels#Extendedconfirmed|extended confirmed users]] to create new pages.
| autoconfirmed = Autoconfirmed users can't edit this {{NAMESPACE}} page, but still be able to edit pages by users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will </span>be [[WP:User access levels#Autoconfirmed users|autoconfirmed users]] to create new pages. (After 4 days & 10 edits on English Wikipedia.)
| user = Some users can't edit this {{NAMESPACE}} page, but still be able to edit pages by anonymous users & temporally accounts. You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] to make this page. {{#ifeq:{{NAMESPACE}}|{{ns:Category}}|Or, you can [[Wikipedia:Articles for creation/Categories|request the creation of a new category]].}}
| #default = Users with the "Create" permission can't edit this {{NAMESPACE}} page. (You do not have permission to create this page when not logged in). You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will be [[WP:User access levels#Autoconfirmed users|autoconfirmed users]] to make your own page.
}}
|{{#ifeq:{{#invoke:Title blacklist|main|action=create|pagename={{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}|templateeditor|* Administrators, template editors, & page movers can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will be [[Wikipedia:Administrators|administrators]], [[Wikipedia:Template editors|template editors]], & [[Wikipedia:Page movers|page movers]] to create new ones.|* ''' {{ #switch: {{NAMESPACE}}
| {{ns:3}} = {{#ifeq:{{ROOTPAGENAME}}|{{PAGENAME}}|[[Special:Edit/{{FULLPAGENAME}}|Post a message to ''{{#invoke:String|replace|source={{PAGENAME}}|}}]]''|[[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|leave a message about the ''{{#invoke:String|replace|source={{SUBJECTPAGENAME}}|}}]]''}}
| {{ns:5}}
| {{ns:7}}
| {{ns:9}}
| {{ns:11}}
| {{ns:13}}
| {{ns:15}}
| {{ns:101}}
| {{ns:119}}
| {{ns:711}}
| {{ns:829}}
| {{ns:1}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|leave a message about the ''{{#invoke:String|replace|source={{SUBJECTPAGENAME}}|}}]]''
| {{ns:Category}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'']]
| {{ns:Project}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|start a discussion about the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}]]''
| #default = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]]
}}''' {{ #if: {{NAMESPACE}}
|by using the '''"[[Wikipedia:Namespaces|{{NAMESPACE}}]]"''' namespace|by using the '''"[[Wikipedia:Namespaces|Page]]"''' namespace, using the [[Wikipedia:Article wizard|Article Wizard]] if you want to create it & submit it for review, or [[{{ns:Project}}:Requested articles|add a request for the new page]]
}}.}}
}}{{ #switch: {{NAMESPACE}}
| {{ns:0}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing pages.
* [[wiktionary:en:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wiktionary, our sister dictionary project.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:User}} =
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this user page]].
| {{ns:User talk}} =
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this user talk page]].
| {{ns:Category}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing categories.
* [[wiktionary:en:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wiktionary, our sister dictionary project.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:File}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing files.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:Template}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing templates.
* [[wikimedia:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wikimedia, the Free Templates, Categories, & More!
| #default =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing {{#if: {{NAMESPACE}} | pages with the '''"[[{{ns:Project}}:Namespaces|{{NAMESPACE}}]]"''' namespace | pages without using [[{{ns:Project}}:Namespaces|namespaces]] }}.
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this title]].
}}
* {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} | You can go back to your [[:{{SUBJECTPAGENAME}}|subject page]] | Please discuss changes on your [[{{TALKPAGENAME}}|talk page]] }}.
<div id="noarticletext_technical">
----
'''Other reasons that may be displayed this message:'''
* If {{ #ifeq: {{NAMESPACE}} | {{ns:File}} | a file | a page }} was recently edited here, it may be invisible. (Because of a delay in updating the database.) You can wait a few minutes to complete, you can [[Special:Purge/{{FULLPAGENAME}}|click it]] for purging, or you may want to [[Special:Protect/{{FULLPAGENAME}}|protect]] this page. You can also want to [[Special:Edit/{{FULLPAGENAME}}|edit this page]] or [[Special:Delete/{{FULLPAGENAME}}|delete this page]].
* Titles on the Test Wikipedia is in the '''[[w:en:Case sensitivity|case sensitivity]]''' except for this first character; please {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1|name=check the Test Wikipedia's alternative capitalizations}} and consider adding some [[Wikipedia:Redirects|redirects]] here to the correct title. If the page you are looking for is not TestSite, see its corresponding [[w:en:Main Page|language Wikipedia]]. If the page is not a [[w:en:test wiki|test wiki]], check one of the [[wikimedia:Main Page|other Foundation wikis]].
* If this page has been deleted, try [{{fullurl:Special:Log/delete|page={{#invoke:String|replace|source={{FULLPAGENAMEE}}|}}}} checking the '''deletion log'''], and see [[Wikipedia:Why was the page I created deleted?|the Why was the page I created deleted?]] for possible reasons.
</div>
}}
<!--End fmbox --><!--Start book box-->{{#if:{{#invoke:String|match|{{FULLPAGENAME}}|^Book:|nomatch= }}{{#invoke:String|match|{{FULLPAGENAME}}|^Book talk:|nomatch= }}|{{fmbox|type=system|text=<strong>This page may have existed as part of the former book & talk namespace.</strong> If so it was likely moved to [[{{#invoke:String|replace|{{#invoke:String|replace|{{FULLPAGENAME}}|^Book talk:|Book:|plain=False}}|^Book:|Wikipedia:Books/archive/|plain=False}}]] before being deleted. See [[Wikipedia:Books]] for more information.}}}}<!--End book box-->
</div>
== {{ #switch: {{NAMESPACE}}
| {{ns:0}} = Page
| {{ns:Talk}} = [[{{ns:Help}}:Using talk pages|Discussion page]]
| {{TALKSPACE}} = [[{{ns:Help}}:Using talk pages|{{SUBJECTSPACE}}'s Discussion page]]
| {{ns:Category}} = [[{{ns:Project}}:Categorization|Category]]
| {{ns:Help}} = [[{{ns:Help}}:Contents|Help page]]
| {{ns:File}} = [[{{ns:Project}}:Files to upload|File]]
| {{ns:Portal}} = [[{{ns:Project}}:Portal|Portal]]
| {{ns:Template}} = [[{{ns:Project}}:Template messages|Template]]
| {{ns:User}} = [[{{ns:Project}}:User pages|User page]]
| {{ns:Project}} = [[{{ns:Project}}:Project namespace|Project page]]
| {{ns:MediaWiki}} = [[Special:AllMessages|System message]]
| #default = {{NAMESPACE}} page}} not showing up? ==
Reload the page you just created "'''{{FULLPAGENAME}}'''."
<small>You may also [[Wikipedia:Administrators' noticeboard|contact an administrator]] before recreating this page.</small>
If you can't find the page, you can [[Special:Search/{{FULLPAGENAME}}|search for an existing page]] or [[Special:Edit/{{FULLPAGENAME}}|create it]].
{{ #ifeq: {{{nopermission}}} | yes |<div class="mw-parser-output">{{fmbox
| type = warning
| id = noarticletext
| image = none
| text = You do not have permission to create this page. It may have been protected from creation. If I think this page should be created here, please unprotect this page at [[Wikipedia:Administrators' noticeboard|the Administrators' noticeboard]].}}</div>|<div class="mw-parser-output">{{fmbox
| type = system
| id = noarticletext
| image = none
| text = If you can't find {{FULLPAGENAME}}, you can also include the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|veaction=edit}} visual editor]</span>, {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} | the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit&veswitched=1}} source editor]</span>, or [[Special:NewSection/{{FULLPAGENAME}}|create a new section]] | or the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit&veswitched=1}} source editor]</span> }} for editing.}}</div>}}
<noinclude>
{{documentation}}
</noinclude>
4t261j5qsz391mp2d93wjpatb6nuarp
739056
739054
2026-04-22T00:20:28Z
Sheffalump
71152
739056
wikitext
text/x-wiki
<div class="mw-parser-output"><!-- don't touch this div to the left, it makes TemplateStyles vroom vroom -->
{{New page DYM}} {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} |{{#ifexist: {{SUBJECTPAGENAME}}|This is the talk page for [[:{{SUBJECTPAGENAME}}]].|[[:{{SUBJECTPAGENAME}}]] has not yet been created. {{Viewing talk page without subject}}|}}}} {{fmbox
| type = system
| id = noarticletext
| image = none
| textstyle = padding: 0.6em 0.9em; <!--Large box needs more padding-->
| text = {{#tag:strong|WARNING:}} {{ #ifeq: {{ #ifeq: {{FULLPAGENAME}} | Template:No article text || }} | <!--Mainspace sister project infobox-->
| {{No article text/sister projects}}
}}{{ #ifeq: {{NAMESPACE}} | {{ns:User talk}}
| {{#tag:strong|No messages}} have been posted for {{BASEPAGENAME}} yet.
| {{#tag:strong|Wikipedia does not contain {{ #switch: {{NAMESPACE}}
| {{ns:0}} = a {{#tag:em|[[{{ns:Project}}:Using pages without namespaces|page]]}}
| {{ns:Talk}} = a {{#tag:em|[[{{ns:Help}}:Using talk pages|discussion page]]}}
| {{TALKSPACE}} = the {{#tag:em|[[{{ns:Help}}:Using talk pages|{{SUBJECTSPACE}}'s discussion page]]}}
| {{ns:Category}} = a {{#tag:em|[[{{ns:Project}}:Categorization|category]]}}
| {{ns:Help}} = a {{#tag:em|[[{{ns:Help}}:Contents|help page]]}}
| {{ns:File}} = a {{#tag:em|[[{{ns:Project}}:Files to upload|file]]}}
| {{ns:Portal}} = a {{#tag:em|[[{{ns:Project}}:Portal|portal]]}}
| {{ns:Template}} = a {{#tag:em|[[{{ns:Project}}:Template messages|template]]}}
| {{ns:User}} = a {{#tag:em|[[{{ns:Project}}:User pages|user page]]}}
| {{ns:Project}} = a {{#tag:em|[[{{ns:Project}}:Project namespace|project page]]}}
| {{ns:MediaWiki}} = a {{#tag:em|[[Special:AllMessages|system message]]}}
| #default = a {{#tag:em|[[{{ns:Project}}:Namespaces|{{NAMESPACE}} page]]}}
}} with this exact name yet.}} {{ #ifeq: {{#invoke:String|replace|source={{PAGENAME}}|}} | {{#invoke:String|replace|source={{SUBPAGENAME}}|}}
| {{ #switch: {{NAMESPACE}}
| {{ns:0}} = Please {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1|name=search for ''{{#invoke:String|replace|source={{PAGENAME}}|}}'' in Test Wikipedia}} to check for alternative titles, names, or spellings.
| {{ns:Talk}} = Before creating this page, please verify that a page called [[{{PAGENAME}}]] exists.
| {{TALKSPACE}} = Before creating this page, please verify that [[:{{SUBJECTPAGENAME}}]] is already made.
| {{ns:Category}} = Please browse the [[Portal:Contents|existing categories]] to check if this category is covered under another name.
| {{ns:Help}} = Please browse the [[{{ns:Help}}:Contents|existing help pages]] to check if this help topic is covered under another name.
| {{ns:File}} = Please do not manually create this page. If you want to upload ''File:{{PAGENAME}}'', Please see the [[{{ns:Project}}:Files to upload|Files to upload]] for instructions & tutorials.
| {{ns:Portal}} = Please browse the [[{{ns:Portal}}:Contents/Portals|existing portals]] to check for similar topics.
| {{ns:Template}} = Please browse the [[{{ns:Project}}:Template messages|existing templates]] to check if this standardized message is available under another name.
| {{ns:User}} = In general, this page should be created and edited by [[User:{{PAGENAME}}]]. If you're already registered, Please [[Special:UserLogin|log in]]. If you don't have an account, [[Special:CreateAccount|create one]]. If you are {{PAGENAME}}, click [[Special:Edit/User:{{PAGENAME}}|create this page]]. If you are not {{PAGENAME}}, don't click "create this page".
| {{ns:Project}} = Please browse the [[{{ns:Project}}:List of policies and guidelines|existing policies and guidelines]] or [[Special:Search/{{ns:Project}}:{{PAGENAME}}|search]] for similar existing project pages.
| {{ns:MediaWiki}} = Please browse the [[Special:AllMessages|existing system messages]] to check if this Non-CSS, Non-JavaScript, Non-JSON, CSS, JavaScript, & JSON pages are available under another name.
| #default = Please browse the [[{{ns:Project}}:Namespaces|existing {{NAMESPACE}} pages]] to check if this {{NAMESPACE}} namespace is available under another name.
}}
| {{ #ifeq: {{{nopermission}}} | yes |You do not have permission to edit pages in the {{#if: {{NAMESPACE}} | '''[[Wikipedia:Namespaces|{{NAMESPACE}}]]''' namespace | '''[[Wikipedia:Namespaces|Page]]''' namespace }}. Please [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in] or [{{fullurl:Special:UserLogin/signup|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} sign up] if you want to create this page. (This page is currently protected so only administrators, system administrators, template editors, extended confirmed users, page movers, autoconfirmed users, & logged in users can make it, '''not''' temporary accounts or unregistered (IP) users). (If you're logged out, {{SITENAME}} has restricted the ability to create new pages. You can go back and edit an existing page, or [[Special:UserLogin|log in or create an account]]. If you're logged in, You do not have permission to create new pages.) Please see [[{{ns:Project}}:Subpages|Subpages]] for subpages if you do not create it because this isn't needed. To protect the page, click [[Special:Protect/{{FULLPAGENAME}}|protect]].| Before creating this page in the {{#if: {{NAMESPACE}} | '''[[Wikipedia:Namespaces|{{NAMESPACE}}]]''' namespace | '''[[Wikipedia:Namespaces|Page]]''' namespace }}, please see [[{{ns:Project}}:Subpages|Subpages]] for subpages. To protect the page, click [[Special:Protect/{{FULLPAGENAME}}|protect]].}}
}}
}}{{ #ifeq: {{{nopermission}}} | yes
|* {{ #switch: {{#invoke:Effective protection level|create|{{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}
| sysop = Administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[Wikipedia:Administrators|administrators]] to create new pages, system messages, & JSON pages.
| interfaceadmin = System administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will </span>be [[Wikipedia:System administrators|system administrators]] to create new CSS & JavaScript pages.
| templateeditor = Template editors & administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[WP:Template editor|template editors]] to create new pages.
| extendedconfirmed = Extended confirmed users & administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[Wikipedia:User access levels#Extendedconfirmed|extended confirmed users]] to create new pages.
| autoconfirmed = Autoconfirmed users can't edit this {{NAMESPACE}} page, but still be able to edit pages by users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will </span>be [[WP:User access levels#Autoconfirmed users|autoconfirmed users]] to create new pages. (After 4 days & 10 edits on English Wikipedia.)
| user = Some users can't edit this {{NAMESPACE}} page, but still be able to edit pages by anonymous users & temporally accounts. You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] to make this page. {{#ifeq:{{NAMESPACE}}|{{ns:Category}}|Or, you can [[Wikipedia:Articles for creation/Categories|request the creation of a new category]].}}
| #default = Users with the "Create" permission can't edit this {{NAMESPACE}} page. (You do not have permission to create this page when not logged in). You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will be [[WP:User access levels#Autoconfirmed users|autoconfirmed users]] to make your own page.
}}
|{{#ifeq:{{#invoke:Title blacklist|main|action=create|pagename={{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}|templateeditor|* Administrators, template editors, & page movers can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will be [[Wikipedia:Administrators|administrators]], [[Wikipedia:Template editors|template editors]], & [[Wikipedia:Page movers|page movers]] to create new ones.|* ''' {{ #switch: {{NAMESPACE}}
| {{ns:3}} = {{#ifeq:{{ROOTPAGENAME}}|{{PAGENAME}}|[[Special:Edit/{{FULLPAGENAME}}|Post a message to ''{{#invoke:String|replace|source={{PAGENAME}}|}}]]''|[[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|leave a message about the ''{{#invoke:String|replace|source={{SUBJECTPAGENAME}}|}}]]''}}
| {{ns:5}}
| {{ns:7}}
| {{ns:9}}
| {{ns:11}}
| {{ns:13}}
| {{ns:15}}
| {{ns:101}}
| {{ns:119}}
| {{ns:711}}
| {{ns:829}}
| {{ns:1}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|leave a message about the ''{{#invoke:String|replace|source={{SUBJECTPAGENAME}}|}}]]''
| {{ns:Category}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'']]
| {{ns:Project}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|start a discussion about the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}]]''
| #default = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]]
}}''' {{ #if: {{NAMESPACE}}
|by using the '''"[[Wikipedia:Namespaces|{{NAMESPACE}}]]"''' namespace|by using the '''"[[Wikipedia:Namespaces|Page]]"''' namespace, using the [[Wikipedia:Article wizard|Article Wizard]] if you want to create it & submit it for review, or [[{{ns:Project}}:Requested articles|add a request for the new page]]
}}.}}
}}{{ #switch: {{NAMESPACE}}
| {{ns:0}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing pages.
* [[wiktionary:en:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wiktionary, our sister dictionary project.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:User}} =
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this user page]].
| {{ns:User talk}} =
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this user talk page]].
| {{ns:Category}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing categories.
* [[wiktionary:en:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wiktionary, our sister dictionary project.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:File}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing files.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:Template}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing templates.
* [[wikimedia:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wikimedia, the Free Templates, Categories, & More!
| #default =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing {{#if: {{NAMESPACE}} | pages with the '''"[[{{ns:Project}}:Namespaces|{{NAMESPACE}}]]"''' namespace | pages without using [[{{ns:Project}}:Namespaces|namespaces]] }}.
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this title]].
}}
* {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} | You can go back to your [[:{{SUBJECTPAGENAME}}|subject page]] | Please discuss changes on your [[{{TALKPAGENAME}}|talk page]] }}.
<div id="noarticletext_technical">
----
'''Other reasons that may be displayed this message:'''
* If {{ #ifeq: {{NAMESPACE}} | {{ns:File}} | a file | a page }} was recently edited here, it may be invisible. (Because of a delay in updating the database.) You can wait a few minutes to complete, you can [[Special:Purge/{{FULLPAGENAME}}|click it]] for purging, or you may want to [[Special:Protect/{{FULLPAGENAME}}|protect]] this page. You can also want to [[Special:Edit/{{FULLPAGENAME}}|edit this page]] or [[Special:Delete/{{FULLPAGENAME}}|delete this page]].
* Titles on the Test Wikipedia is in the '''[[w:en:Case sensitivity|case sensitivity]]''' except for this first character; please {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1|name=check the Test Wikipedia's alternative capitalizations}} and consider adding some [[Wikipedia:Redirects|redirects]] here to the correct title. If the page you are looking for is not TestSite, see its corresponding [[w:en:Main Page|language Wikipedia]]. If the page is not a [[w:en:test wiki|test wiki]], check one of the [[wikimedia:Main Page|other Foundation wikis]].
* If this page has been deleted, try [{{fullurl:Special:Log/delete|page={{#invoke:String|replace|source={{FULLPAGENAMEE}}|}}}} checking the '''deletion log'''], and see [[Wikipedia:Why was the page I created deleted?|the Why was the page I created deleted?]] for possible reasons.
</div>
}}
<!--End fmbox --><!--Start book box-->{{#if:{{#invoke:String|match|{{FULLPAGENAME}}|^Book:|nomatch= }}{{#invoke:String|match|{{FULLPAGENAME}}|^Book talk:|nomatch= }}|{{fmbox|type=system|text=<strong>This page may have existed as part of the former book & talk namespace.</strong> If so it was likely moved to [[{{#invoke:String|replace|{{#invoke:String|replace|{{FULLPAGENAME}}|^Book talk:|Book:|plain=False}}|^Book:|Wikipedia:Books/archive/|plain=False}}]] before being deleted. See [[Wikipedia:Books]] for more information.}}}}<!--End book box-->
</div>
== {{ #switch: {{NAMESPACE}}
| {{ns:0}} = Page
| {{ns:Talk}} = [[{{ns:Help}}:Using talk pages|Discussion page]]
| {{TALKSPACE}} = [[{{ns:Help}}:Using talk pages|{{SUBJECTSPACE}}'s Discussion page]]
| {{ns:Category}} = [[{{ns:Project}}:Categorization|Category]]
| {{ns:Help}} = [[{{ns:Help}}:Contents|Help page]]
| {{ns:File}} = [[{{ns:Project}}:Files to upload|File]]
| {{ns:Portal}} = [[{{ns:Project}}:Portal|Portal]]
| {{ns:Template}} = [[{{ns:Project}}:Template messages|Template]]
| {{ns:User}} = [[{{ns:Project}}:User pages|User page]]
| {{ns:Project}} = [[{{ns:Project}}:Project namespace|Project page]]
| {{ns:MediaWiki}} = [[Special:AllMessages|System message]]
| #default = {{NAMESPACE}} page}} not showing up? ==
Reload the page you just created "'''{{FULLPAGENAME}}'''."
<small>You may also [[Wikipedia:Administrators' noticeboard|contact an administrator]] before recreating this page.</small>
If you can't find the page, you can [[Special:Search/{{FULLPAGENAME}}|search for an existing page]] or {{ #ifeq: {{{nopermission}}} | yes | <span class="plainlinks>[{{fullurl:Special:Log|page={{FULLPAGENAME}}}} look for the logs]</span> | [[Special:Edit/{{FULLPAGENAME}}|create it]] }}.
{{ #ifeq: {{{nopermission}}} | yes |<div class="mw-parser-output">{{fmbox
| type = warning
| id = noarticletext
| image = none
| text = You do not have permission to create this page. It may have been protected from creation. If I think this page should be created here, please unprotect this page at [[Wikipedia:Administrators' noticeboard|the Administrators' noticeboard]].}}</div>|<div class="mw-parser-output">{{fmbox
| type = system
| id = noarticletext
| image = none
| text = If you can't find {{FULLPAGENAME}}, you can also include the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|veaction=edit}} visual editor]</span>, {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} | the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit&veswitched=1}} source editor]</span>, or [[Special:NewSection/{{FULLPAGENAME}}|create a new section]] | or the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit&veswitched=1}} source editor]</span> }} for editing.}}</div>}}
<noinclude>
{{documentation}}
</noinclude>
9d9wtld1ry0ciwq10ha0tfsks50admr
739057
739056
2026-04-22T00:23:37Z
Sheffalump
71152
739057
wikitext
text/x-wiki
<div class="mw-parser-output"><!-- don't touch this div to the left, it makes TemplateStyles vroom vroom -->
{{New page DYM}} {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} |{{#ifexist: {{SUBJECTPAGENAME}}|This is the talk page for [[:{{SUBJECTPAGENAME}}]].|[[:{{SUBJECTPAGENAME}}]] has not yet been created. {{Viewing talk page without subject}}|}}}} {{fmbox
| type = system
| id = noarticletext
| image = none
| textstyle = padding: 0.6em 0.9em; <!--Large box needs more padding-->
| text = {{#tag:strong|WARNING:}} {{ #ifeq: {{ #ifeq: {{FULLPAGENAME}} | Template:No article text || }} | <!--Mainspace sister project infobox-->
| {{No article text/sister projects}}
}}{{ #ifeq: {{NAMESPACE}} | {{ns:User talk}}
| {{#tag:strong|No messages}} have been posted for {{BASEPAGENAME}} yet.
| {{#tag:strong|Wikipedia does not contain {{ #switch: {{NAMESPACE}}
| {{ns:0}} = a {{#tag:em|[[{{ns:Project}}:Using pages without namespaces|page]]}}
| {{ns:Talk}} = a {{#tag:em|[[{{ns:Help}}:Using talk pages|discussion page]]}}
| {{TALKSPACE}} = the {{#tag:em|[[{{ns:Help}}:Using talk pages|{{SUBJECTSPACE}}'s discussion page]]}}
| {{ns:Category}} = a {{#tag:em|[[{{ns:Project}}:Categorization|category]]}}
| {{ns:Help}} = a {{#tag:em|[[{{ns:Help}}:Contents|help page]]}}
| {{ns:File}} = a {{#tag:em|[[{{ns:Project}}:Files to upload|file]]}}
| {{ns:Portal}} = a {{#tag:em|[[{{ns:Project}}:Portal|portal]]}}
| {{ns:Template}} = a {{#tag:em|[[{{ns:Project}}:Template messages|template]]}}
| {{ns:User}} = a {{#tag:em|[[{{ns:Project}}:User pages|user page]]}}
| {{ns:Project}} = a {{#tag:em|[[{{ns:Project}}:Project namespace|project page]]}}
| {{ns:MediaWiki}} = a {{#tag:em|[[Special:AllMessages|system message]]}}
| #default = a {{#tag:em|[[{{ns:Project}}:Namespaces|{{NAMESPACE}} page]]}}
}} with this exact name yet.}} {{ #ifeq: {{#invoke:String|replace|source={{PAGENAME}}|}} | {{#invoke:String|replace|source={{SUBPAGENAME}}|}}
| {{ #switch: {{NAMESPACE}}
| {{ns:0}} = Please {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1|name=search for ''{{#invoke:String|replace|source={{PAGENAME}}|}}'' in Test Wikipedia}} to check for alternative titles, names, or spellings.
| {{ns:Talk}} = Before creating this page, please verify that a page called [[{{PAGENAME}}]] exists.
| {{TALKSPACE}} = Before creating this page, please verify that [[:{{SUBJECTPAGENAME}}]] is already made.
| {{ns:Category}} = Please browse the [[Portal:Contents|existing categories]] to check if this category is covered under another name.
| {{ns:Help}} = Please browse the [[{{ns:Help}}:Contents|existing help pages]] to check if this help topic is covered under another name.
| {{ns:File}} = Please do not manually create this page. If you want to upload ''File:{{PAGENAME}}'', Please see the [[{{ns:Project}}:Files to upload|Files to upload]] for instructions & tutorials.
| {{ns:Portal}} = Please browse the [[{{ns:Portal}}:Contents/Portals|existing portals]] to check for similar topics.
| {{ns:Template}} = Please browse the [[{{ns:Project}}:Template messages|existing templates]] to check if this standardized message is available under another name.
| {{ns:User}} = In general, this page should be created and edited by [[User:{{PAGENAME}}]]. If you're already registered, Please [[Special:UserLogin|log in]]. If you don't have an account, [[Special:CreateAccount|create one]]. If you are {{PAGENAME}}, click [[Special:Edit/User:{{PAGENAME}}|create this page]]. If you are not {{PAGENAME}}, don't click "create this page".
| {{ns:Project}} = Please browse the [[{{ns:Project}}:List of policies and guidelines|existing policies and guidelines]] or [[Special:Search/{{ns:Project}}:{{PAGENAME}}|search]] for similar existing project pages.
| {{ns:MediaWiki}} = Please browse the [[Special:AllMessages|existing system messages]] to check if this Non-CSS, Non-JavaScript, Non-JSON, CSS, JavaScript, & JSON pages are available under another name.
| #default = Please browse the [[{{ns:Project}}:Namespaces|existing {{NAMESPACE}} pages]] to check if this {{NAMESPACE}} namespace is available under another name.
}}
| {{ #ifeq: {{{nopermission}}} | yes |You do not have permission to edit pages in the {{#if: {{NAMESPACE}} | '''[[Wikipedia:Namespaces|{{NAMESPACE}}]]''' namespace | '''[[Wikipedia:Namespaces|Page]]''' namespace }}. Please [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in] or [{{fullurl:Special:UserLogin/signup|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} sign up] if you want to create this page. (This page is currently protected so only administrators, system administrators, template editors, extended confirmed users, page movers, autoconfirmed users, & logged in users can make it, '''not''' temporary accounts or unregistered (IP) users). (If you're logged out, {{SITENAME}} has restricted the ability to create new pages. You can go back and edit an existing page, or [[Special:UserLogin|log in or create an account]]. If you're logged in, You do not have permission to create new pages.) Please see [[{{ns:Project}}:Subpages|Subpages]] for subpages if you do not create it because this isn't needed. To protect the page, click [[Special:Protect/{{FULLPAGENAME}}|protect]].| Before creating this page in the {{#if: {{NAMESPACE}} | '''[[Wikipedia:Namespaces|{{NAMESPACE}}]]''' namespace | '''[[Wikipedia:Namespaces|Page]]''' namespace }}, please see [[{{ns:Project}}:Subpages|Subpages]] for subpages. To protect the page, click [[Special:Protect/{{FULLPAGENAME}}|protect]].}}
}}
}}{{ #ifeq: {{{nopermission}}} | yes
|* {{ #switch: {{#invoke:Effective protection level|create|{{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}
| sysop = Administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[Wikipedia:Administrators|administrators]] to create new pages, system messages, & JSON pages.
| interfaceadmin = System administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will </span>be [[Wikipedia:System administrators|system administrators]] to create new CSS & JavaScript pages.
| templateeditor = Template editors & administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[WP:Template editor|template editors]] to create new pages.
| extendedconfirmed = Extended confirmed users & administrators can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will</span> be [[Wikipedia:User access levels#Extendedconfirmed|extended confirmed users]] to create new pages.
| autoconfirmed = Autoconfirmed users can't edit this {{NAMESPACE}} page, but still be able to edit pages by users, anonymous users, & temporally accounts. You will <span class="anononly">need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will </span>be [[WP:User access levels#Autoconfirmed users|autoconfirmed users]] to create new pages. (After 4 days & 10 edits on English Wikipedia.)
| user = Some users can't edit this {{NAMESPACE}} page, but still be able to edit pages by anonymous users & temporally accounts. You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] to make this page. {{#ifeq:{{NAMESPACE}}|{{ns:Category}}|Or, you can [[Wikipedia:Articles for creation/Categories|request the creation of a new category]].}}
| #default = Users with the "Create" permission can't edit this {{NAMESPACE}} page. (You do not have permission to create this page when not logged in). You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will be [[WP:User access levels#Autoconfirmed users|autoconfirmed users]] to make your own page.
}}
|{{#ifeq:{{#invoke:Title blacklist|main|action=create|pagename={{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}|templateeditor|* Administrators, template editors, & page movers can't edit this {{NAMESPACE}} page, but still be able to edit pages by autoconfirmed users, users, anonymous users, & temporally accounts. You will need to [{{fullurl:Special:UserLogin|returnto={{ urlencode: {{#invoke:String|replace|source={{FULLPAGENAME}}|}} }}}} log in or create an account] and will be [[Wikipedia:Administrators|administrators]], [[Wikipedia:Template editors|template editors]], & [[Wikipedia:Page movers|page movers]] to create new ones.|* ''' {{ #switch: {{NAMESPACE}}
| {{ns:3}} = {{#ifeq:{{ROOTPAGENAME}}|{{PAGENAME}}|[[Special:Edit/{{FULLPAGENAME}}|Post a message to ''{{#invoke:String|replace|source={{PAGENAME}}|}}]]''|[[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|leave a message about the ''{{#invoke:String|replace|source={{SUBJECTPAGENAME}}|}}]]''}}
| {{ns:5}}
| {{ns:7}}
| {{ns:9}}
| {{ns:11}}
| {{ns:13}}
| {{ns:15}}
| {{ns:101}}
| {{ns:119}}
| {{ns:711}}
| {{ns:829}}
| {{ns:1}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|leave a message about the ''{{#invoke:String|replace|source={{SUBJECTPAGENAME}}|}}]]''
| {{ns:Category}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'']]
| {{ns:Project}} = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]] or [[Special:NewSection/{{FULLPAGENAME}}|start a discussion about the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}]]''
| #default = [[Special:Edit/{{FULLPAGENAME}}|Start the ''{{#invoke:String|replace|source={{FULLPAGENAME}}|}}'' page]]
}}''' {{ #if: {{NAMESPACE}}
|by using the '''"[[Wikipedia:Namespaces|{{NAMESPACE}}]]"''' namespace|by using the '''"[[Wikipedia:Namespaces|Page]]"''' namespace, using the [[Wikipedia:Article wizard|Article Wizard]] if you want to create it & submit it for review, or [[{{ns:Project}}:Requested articles|add a request for the new page]]
}}.}}
}}{{ #switch: {{NAMESPACE}}
| {{ns:0}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing pages.
* [[wiktionary:en:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wiktionary, our sister dictionary project.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:User}} =
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this user page]].
| {{ns:User talk}} =
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this user talk page]].
| {{ns:Category}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing categories.
* [[wiktionary:en:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wiktionary, our sister dictionary project.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:File}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing files.
* [[commons:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in the Wikimedia Commons, our repository for free images, music, sound, and video.
| {{ns:Template}} =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing templates.
* [[wikimedia:Special:Search/{{FULLPAGENAME}}|Look for "{{FULLPAGENAME}}"]] in Wikimedia, the Free Templates, Categories, & More!
| #default =
* {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{PAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1&ns{{NAMESPACENUMBER}}=1|name=Search for "''{{#invoke:String|replace|source={{PAGENAME}}|}}''"}} in existing {{#if: {{NAMESPACE}} | pages with the '''"[[{{ns:Project}}:Namespaces|{{NAMESPACE}}]]"''' namespace | pages without using [[{{ns:Project}}:Namespaces|namespaces]] }}.
* [[{{ns:Special}}:WhatLinksHere/{{#invoke:String|replace|source={{FULLPAGENAME}}|}}|Look for pages within the Test Wikipedia that link to this title]].
}}
* {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} | You can go back to your [[:{{SUBJECTPAGENAME}}|subject page]] | Please discuss changes on your [[{{TALKPAGENAME}}|talk page]] }}.
<div id="noarticletext_technical">
----
'''Other reasons that may be displayed this message:'''
* If {{ #ifeq: {{NAMESPACE}} | {{ns:File}} | a file | a page }} was recently edited here, it may be invisible. (Because of a delay in updating the database.) You can wait a few minutes to complete, you can [[Special:Purge/{{FULLPAGENAME}}|click it]] for purging, or you may want to [[Special:Protect/{{FULLPAGENAME}}|protect]] this page. You can also want to [[Special:Edit/{{FULLPAGENAME}}|edit this page]] or [[Special:Delete/{{FULLPAGENAME}}|delete this page]].
* Titles on the Test Wikipedia is in the '''[[w:en:Case sensitivity|case sensitivity]]''' except for this first character; please {{plain link|url=https://test.wikipedia.org/w/index.php?search={{urlencode:{{#titleparts:{{#invoke:String|replace|source={{FULLPAGENAME}}|}}}}}}&title=Special%3ASearch&fulltext=1|name=check the Test Wikipedia's alternative capitalizations}} and consider adding some [[Wikipedia:Redirects|redirects]] here to the correct title. If the page you are looking for is not TestSite, see its corresponding [[w:en:Main Page|language Wikipedia]]. If the page is not a [[w:en:test wiki|test wiki]], check one of the [[wikimedia:Main Page|other Foundation wikis]].
* If this page has been deleted, try [{{fullurl:Special:Log/delete|page={{#invoke:String|replace|source={{FULLPAGENAMEE}}|}}}} checking the '''deletion log'''], and see [[Wikipedia:Why was the page I created deleted?|the Why was the page I created deleted?]] for possible reasons.
</div>
}}
<!--End fmbox --><!--Start book box-->{{#if:{{#invoke:String|match|{{FULLPAGENAME}}|^Book:|nomatch= }}{{#invoke:String|match|{{FULLPAGENAME}}|^Book talk:|nomatch= }}|{{fmbox|type=system|text=<strong>This page may have existed as part of the former book & talk namespace.</strong> If so it was likely moved to [[{{#invoke:String|replace|{{#invoke:String|replace|{{FULLPAGENAME}}|^Book talk:|Book:|plain=False}}|^Book:|Wikipedia:Books/archive/|plain=False}}]] before being deleted. See [[Wikipedia:Books]] for more information.}}}}<!--End book box-->
</div>
== {{ #switch: {{NAMESPACE}}
| {{ns:0}} = Page
| {{ns:Talk}} = [[{{ns:Help}}:Using talk pages|Discussion page]]
| {{TALKSPACE}} = [[{{ns:Help}}:Using talk pages|{{SUBJECTSPACE}}'s Discussion page]]
| {{ns:Category}} = [[{{ns:Project}}:Categorization|Category]]
| {{ns:Help}} = [[{{ns:Help}}:Contents|Help page]]
| {{ns:File}} = [[{{ns:Project}}:Files to upload|File]]
| {{ns:Portal}} = [[{{ns:Project}}:Portal|Portal]]
| {{ns:Template}} = [[{{ns:Project}}:Template messages|Template]]
| {{ns:User}} = [[{{ns:Project}}:User pages|User page]]
| {{ns:Project}} = [[{{ns:Project}}:Project namespace|Project page]]
| {{ns:MediaWiki}} = [[Special:AllMessages|System message]]
| #default = {{NAMESPACE}} page}} not showing up? ==
Reload the page you just created "'''{{FULLPAGENAME}}'''."
<small>You may also [[Wikipedia:Administrators' noticeboard|contact an administrator]] before recreating this page.</small>
If you can't find the page, you can [[Special:Search/{{FULLPAGENAME}}|search for an existing page]] or {{ #ifeq: {{{nopermission}}} | yes | <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{urlencode:{{FULLPAGENAME}}}}}} search the related logs]</span> | [[Special:Edit/{{FULLPAGENAME}}|create it]] }}.
{{ #ifeq: {{{nopermission}}} | yes |<div class="mw-parser-output">{{fmbox
| type = warning
| id = noarticletext
| image = none
| text = You do not have permission to create this page. It may have been protected from creation. If I think this page should be created here, please unprotect this page at [[Wikipedia:Administrators' noticeboard|the Administrators' noticeboard]].}}</div>|<div class="mw-parser-output">{{fmbox
| type = system
| id = noarticletext
| image = none
| text = If you can't find {{FULLPAGENAME}}, you can also include the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|veaction=edit}} visual editor]</span>, {{ #ifeq: {{NAMESPACE}} | {{TALKSPACE}} | the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit&veswitched=1}} source editor]</span>, or [[Special:NewSection/{{FULLPAGENAME}}|create a new section]] | or the <span class="planlinks">[{{fullurl:{{FULLPAGENAME}}|action=edit&veswitched=1}} source editor]</span> }} for editing.}}</div>}}
<noinclude>
{{documentation}}
</noinclude>
0m9c6fikoomehqiuch8tn1irscl3450
User:Sam Sailor/test.js
2
98186
739070
739007
2026-04-22T05:57:20Z
Sam Sailor
26820
Test
739070
javascript
text/javascript
//<nowiki>
(function() {
if (window.Headmaster) return;
const defaultNS = [0, 2, 10, 118];
const userNS = window.headmaster_ns || [];
const config = {
menu: window.headmaster_menu || 'p-cactions',
enableSummary: window.headmaster_do_summary !== false,
customSummary: window.headmaster_summary || "",
allowedNamespaces: Array.from(new Set([...defaultNS, ...userNS])),
autoScan: window.headmaster_auto_scan !== false,
autoRun: window.headmaster_auto_run === true
};
const Headmaster = {
VERSION: "0.9.1",
DefaultSummary: "Converting [[MOS:PSEUDOHEAD|pseudo-headings]] {details}using [[User:Sam_Sailor/Scripts/Headmaster.js|Headmaster]]. Semicolon markup is reserved for [[MOS:DLIST|description lists]].",
styleInjected: false,
css: `
#hm-panel { clear: both; border: 1px solid #a2a9b1; padding: 15px; background: #f8f9fa; margin-bottom: 1.5em; border-radius: 2px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
#hm-table { width: 100%; border-collapse: collapse; background: white; margin-top: 10px; }
#hm-table th { background: #f2f2f2; border: 1px solid #a2a9b1; padding: 8px; text-align: left; }
#hm-table td { border: 1px solid #a2a9b1; padding: 8px; vertical-align: middle; }
.hm-context { font-family: 'Courier New', monospace; font-size: 0.9em; color: #54595d; background: #fdfdfd; }
#hm-controls { display: flex; justify-content: space-between; align-items: center; margin-top: 15px; }
#hm-controls button { margin-left: 10px; padding: 6px 16px; cursor: pointer; border-radius: 2px; }
.hm-btn-apply { background: #36c; color: white; border: 1px solid #36c; font-weight: bold; }
.hm-btn-apply:hover { background: #447ff5; }
.hm-global-selector { font-size: 0.9em; color: #202122; background: #eaecf0; padding: 8px; border-radius: 2px; border: 1px solid #a2a9b1; }
.hm-global-selector a { font-weight: bold; text-decoration: none; color: #36c; }
.hm-global-selector a:hover { text-decoration: underline; }
.hm-locate-btn { cursor: pointer; color: #36c; border: 1px solid #a2a9b1; background: #f8f9fa; padding: 2px 6px; font-size: 0.85em; border-radius: 2px; }
.hm-locate-btn:hover { background: #fff; border-color: #36c; }
`,
setup() {
if (!config.allowedNamespaces.includes(mw.config.get("wgNamespaceNumber"))) return;
const action = mw.config.get("wgAction");
this.addPortlet(action);
if (action === 'view' && (config.autoScan || config.autoRun)) {
this.silentScan();
}
if (mw.util.getParamValue('headmaster') === '1' && ['edit', 'submit'].includes(action)) {
const params = new URLSearchParams(window.location.search);
params.delete('headmaster');
const searchString = params.toString();
const newUrl = window.location.pathname + (searchString ? '?' + searchString : '');
window.history.replaceState(null, '', newUrl);
this.init();
}
},
addPortlet(action) {
const link = mw.util.addPortletLink(config.menu, '#', 'Headmaster', 'ca-hm', 'Interactively convert pseudo-headings');
if (link) {
$(link).on('click', (e) => {
e.preventDefault();
if (action === 'view') {
this.redirectToEdit();
} else if (['edit', 'submit'].includes(action)) {
this.init();
}
});
}
},
redirectToEdit() {
window.location.href = mw.util.getUrl(mw.config.get("wgPageName"), {
action: 'edit',
headmaster: '1'
});
},
silentScan() {
new mw.Api().get({
action: 'query',
prop: 'revisions',
titles: mw.config.get('wgPageName'),
rvprop: 'content',
rvlimit: 1,
formatversion: 2
}).done((data) => {
const page = data.query.pages[0];
if (!page || !page.revisions) return;
const wikitext = page.revisions[0].content;
const tasks = this.analyzeText(wikitext);
if (tasks.length > 0) {
if (config.autoRun) {
this.redirectToEdit();
} else {
this.notifyDiscovery(tasks.length);
}
}
});
},
notifyDiscovery(count) {
const $msg = $('<span>').text(`Headmaster: Found ${count} pseudo-headings. `).append($('<a>').text('Run conversion').css({
fontWeight: 'bold',
cursor: 'pointer'
}).on('click', () => this.redirectToEdit()));
mw.notify($msg, {
tag: 'hm-discovery',
autoHide: false
});
},
init() {
if (!this.styleInjected) {
mw.loader.addStyleTag(this.css);
this.styleInjected = true;
}
const $textbox = $('#wpTextbox1');
const wikitext = $textbox.textSelection('getContents');
if (!wikitext) {
mw.notify("Textbox is empty or not found.", {
type: 'error'
});
return;
}
const tasks = this.analyzeText(wikitext);
if (tasks.length === 0) {
mw.notify("No pseudo-headings found.");
return;
}
this.showUI(tasks, wikitext);
},
analyzeText(wikitext) {
const lines = wikitext.split("\n");
const tasks = [];
let currentDepth = 2;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
const headingMatch = line.match(/^(={2,6})\s*.+?\s*\1\s*$/);
if (headingMatch) currentDepth = headingMatch[1].length;
if (line.startsWith(';') && !line.startsWith(';;')) {
const nextLine = lines[i + 1] || "";
if (!nextLine.trim().startsWith(':')) {
const cleanText = lines[i].substring(1).replace(/\s*:.*$/, "").trim();
const suggestedLevel = "=".repeat(Math.min(currentDepth + 1, 6));
tasks.push({
index: i,
original: lines[i],
cleanText: cleanText,
context: nextLine.substring(0, 60).trim() + (nextLine.length > 60 ? "..." : ""),
suggestedLevel: suggestedLevel
});
}
}
}
return tasks;
},
showUI(tasks, originalWikitext) {
$('#hm-panel').remove();
$('#editform').hide();
const tableRows = tasks.map((task, i) => `
<tr data-task-index="${task.index}">
<td><button class="hm-locate-btn" data-hm-i="${i}" title="Show in editor">Locate</button></td>
<td class="hm-context"><code>${mw.html.escape(task.original)}</code></td>
<td><label><input type="radio" name="hm-opt-${i}" value="keep"> Keep</label></td>
<td><label><input type="radio" name="hm-opt-${i}" value="bold" checked> <b>Bold</b></label></td>
<td><label><input type="radio" name="hm-opt-${i}" value="head"> Heading (${task.suggestedLevel.length})</label></td>
<td class="hm-context">${mw.html.escape(task.context || "(end of page)")}</td>
</tr>
`).join("");
const $panel = $(`
<div id="hm-panel">
<h3 style="margin-top:0">Headmaster v${this.VERSION}: Review pseudo-headings</h3>
<div class="hm-global-selector">
<strong>Bulk action:</strong>
<a href="#" id="hm-all-keep">Set all to Keep</a> |
<a href="#" id="hm-all-bold">Set all to Bold</a> |
<a href="#" id="hm-all-head">Set all to Heading</a>
</div>
<table id="hm-table">
<thead>
<tr>
<th>Find</th>
<th>Source</th>
<th colspan="3">Action</th>
<th>Next line context</th>
</tr>
</thead>
<tbody>${tableRows}</tbody>
</table>
<div id="hm-controls">
<span>Found <strong>${tasks.length}</strong> possible pseudo-headings.</span>
<div>
<button id="hm-cancel" class="mw-ui-button">Cancel</button>
<button id="hm-apply" class="hm-btn-apply">Show changes (diff)</button>
</div>
</div>
</div>
`);
$('#content').prepend($panel);
window.scrollTo(0, 0);
$('.hm-locate-btn').on('click', function() {
$(document).off('mousedown.hmlocate');
const idx = $(this).data('hm-i');
const task = tasks[idx];
const lines = originalWikitext.split('\n');
let charOffset = 0;
for (let j = 0; j < task.index; j++) {
charOffset += lines[j].length + 1;
}
$('#hm-panel').hide();
$('#editform').show();
const $textbox = $('#wpTextbox1');
$textbox.textSelection('setSelection', {
start: charOffset,
end: charOffset + lines[task.index].length
});
$textbox.textSelection('scrollToCaretPosition');
const locateNotify = mw.notify("Locating line " + (task.index + 1) + ". Click outside the editor to return.", {
tag: 'hm-locate',
type: 'success',
autoHide: false
});
setTimeout(() => {
$(document).on('mousedown.hmlocate', function(e) {
if (!$(e.target).closest('#editform, .mw-notification-area').length) {
$(document).off('mousedown.hmlocate');
locateNotify.then(n => n.close());
$('#editform').hide();
$('#hm-panel').show();
}
});
}, 200);
});
$('#hm-all-keep').on('click', (e) => {
e.preventDefault();
$('input[value="keep"]').prop('checked', true);
});
$('#hm-all-bold').on('click', (e) => {
e.preventDefault();
$('input[value="bold"]').prop('checked', true);
});
$('#hm-all-head').on('click', (e) => {
e.preventDefault();
$('input[value="head"]').prop('checked', true);
});
$('#hm-cancel').on('click', () => {
$('#hm-panel').remove();
$('#editform').show();
});
$('#hm-apply').on('click', () => this.applyChanges(tasks, originalWikitext));
},
getProcessedSummary(existingSummary, bolds, heads) {
if (!config.enableSummary) return existingSummary;
let mySummary, isDefault = false;
if (config.customSummary) {
mySummary = config.customSummary;
} else {
isDefault = true;
const detailsArr = [];
if (bolds > 0) detailsArr.push(`${bolds} to bold`);
if (heads > 0) detailsArr.push(`${heads} to heading`);
const detailsStr = detailsArr.length > 0 ? `(${detailsArr.join(', ')}) ` : "";
mySummary = this.DefaultSummary.replace("{details}", detailsStr);
}
const trimmedOld = existingSummary.trim();
if (!trimmedOld) return mySummary;
if (/[.!?]$/.test(trimmedOld)) {
return trimmedOld + " " + mySummary;
} else {
const finalSummary = isDefault ? mySummary.charAt(0).toLowerCase() + mySummary.slice(1) : mySummary;
return trimmedOld + "; " + finalSummary;
}
},
applyChanges(tasks, wikitext) {
try {
const lines = wikitext.split("\n");
let bCount = 0,
hCount = 0;
$('#hm-table tbody tr').each(function(i) {
const action = $(this).find('input:checked').val();
const task = tasks[i];
if (action === 'bold') {
lines[task.index] = `'''${task.cleanText}'''`;
bCount++;
} else if (action === 'head') {
const h = task.suggestedLevel;
lines[task.index] = `${h} ${task.cleanText} ${h}`;
hCount++;
}
});
if (bCount + hCount === 0) {
mw.notify("No changes selected.");
$('#hm-panel').remove();
$('#editform').show();
return;
}
const $textbox = $('#wpTextbox1');
const $summary = $('#wpSummary');
$textbox.textSelection('setContents', lines.join("\n"));
$summary.val(this.getProcessedSummary($summary.val(), bCount, hCount));
$('#hm-panel').remove();
$('#editform').show();
$('#wpDiff').click();
} catch (err) {
$('#editform').show();
mw.notify("Headmaster error: " + err.message, {
type: 'error'
});
}
}
};
window.Headmaster = Headmaster;
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'jquery.textSelection'], () => {
$(() => Headmaster.setup());
});
})();
(function() {
var Comrade = Comrade || {};
mw.util.addCSS(`
.ambox-Orphan { display: block !important; }
.comrade-container {
box-shadow: 0 1px 3px rgba(0,0,0,0.1); border-radius: 2px;
padding: 10px 15px; margin: 10px 0; display: flex;
flex-direction: column; align-items: flex-start; gap: 8px;
border: 1px solid #a2a9b1; background: #fcfcfc; line-height: 1.5;
}
.comrade-header { display: flex; align-items: center; justify-content: space-between; width: 100%; }
.comrade-content { display: flex; flex-direction: column; gap: 5px; width: 100%; }
.comrade-success { background-color: #d5f5e3 !important; border-left: 5px solid #27ae60 !important; color: #1b5e20 !important; }
.comrade-nudge { background: #fff9ea; border-left: 5px solid #f0ad4e; }
.comrade-info { background: #eaf3ff; border-left: 5px solid #36c; }
.comrade-status { background: #eaf3ff; border-left: 5px solid #36c; }
.comrade-container button:not(.comrade-dismiss) {
background: #f8f9fa; border: 1px solid #a2a9b1; border-radius: 2px;
padding: 2px 10px; cursor: pointer; font-weight: bold; font-size: 0.95em; margin-left: 10px;
}
.comrade-container button:not(.comrade-dismiss):hover { border-color: #36c; color: #36c; background: #fff; }
.comrade-dismiss {
background: transparent; border: none; color: #d33; font-weight: bold;
cursor: pointer; font-size: 0.9em;
}
.comrade-dismiss:hover { text-decoration: underline; }
.comrade-code-block { background: #f1f1f1; padding: 4px 8px; border-radius: 3px; font-family: monospace; display: inline-block; margin-top: 4px; }
`);
function appendDismiss($el) {
$('<button>').addClass('comrade-dismiss').text('Dismiss').click(function() {
$(this).closest('.comrade-container').fadeOut();
}).appendTo($el.find('.comrade-header'));
$("#siteSub").after($el);
}
async function checkAndRenderRedirect(redirTitle, targetTitle, rCategory, labelText, customWikitext) {
const api = new mw.Api();
const res = await api.get({
action: 'query',
titles: redirTitle,
formatversion: 2
});
if (res.query.pages[0].missing) {
const safeId = "comrade-redir-" + btoa(unescape(encodeURIComponent(redirTitle))).replace(/=/g, "");
const $container = $('<div>').addClass('comrade-container comrade-info').attr('id', safeId);
const $header = $('<div>').addClass('comrade-header');
const summary = `Created redirect from ${labelText.toLowerCase()} to [[${targetTitle}]]`;
const $btn = $('<button>').text('Create redirect').click(() => createModernRedirect(redirTitle, targetTitle, rCategory, summary, safeId, customWikitext));
$header.append($('<div>').append($('<strong>').text(`${labelText}: `), `Create redirect from `, $('<code>').text(redirTitle), $btn));
$container.append($header);
appendDismiss($container);
}
}
async function checkDomainRedirect(qid, currentTitle) {
let domainCandidate = "";
if (qid) {
try {
const res = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetclaims',
entity: qid,
property: 'P856',
format: 'json',
origin: '*'
}
});
if (res.claims?.P856) {
domainCandidate = res.claims.P856[0].mainsnak.datavalue.value;
}
} catch (e) {
console.log("Comrade: Wikidata API call failed.");
}
}
if (!domainCandidate) {
domainCandidate = $(".infobox .url a").last().attr("href") || $(".official-website a").first().attr("href");
}
if (domainCandidate) {
try {
const url = new URL(domainCandidate);
let domain = url.hostname.replace(/^www\d*\./, "");
if (domain.includes(".") && url.pathname === "/") {
const citationURL = '/api/rest_v1/data/citation/mediawiki/' + encodeURIComponent(domainCandidate);
$.ajax({
url: citationURL,
method: 'GET',
success: function() {
checkAndRenderRedirect(domain, currentTitle, "R from domain name", "Domain");
}
});
}
} catch (e) {
console.log("Comrade: Error parsing URL object.");
}
}
}
async function checkNameList(name, type, currentTitle, hasShortDesc, isOrphan) {
const api = new mw.Api();
const candidates = [`${name} (${type})`, `${name} (${type.toLowerCase()})`, `${name} (name)`, name];
const checked = new Set();
for (const title of candidates) {
if (checked.has(title)) continue;
checked.add(title);
const res = await api.get({
action: 'query',
prop: 'revisions',
titles: title,
rvprop: 'content',
formatversion: 2,
redirects: 0
});
const page = res.query.pages[0];
if (page && !page.missing) {
const text = page.revisions[0].content;
const resolvedTitle = page.title;
const isNameMatch = /\{\{(Surname|Hndis|Given[ _]name|Forename|Givenname)/i.test(text);
const isNameDab = /\{\{(Disambiguation|Dab|Disambig)(?:[^}]*\|(surname|surnames|given[ _]name|given[ _]names)|(?:\s*\}\}))/i.test(text);
if (isNameMatch || isNameDab) {
const escapedTitle = currentTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/ /g, '[ _]');
const alreadyListed = new RegExp('(\\[\\[|\\{\\{anbl\\|[ _]*|\\|)' + escapedTitle, 'i').test(text);
if (alreadyListed) break;
const $container = $('<div>').addClass('comrade-container');
const $header = $('<div>').addClass('comrade-header');
const $content = $('<div>').addClass('comrade-content');
if (isOrphan) {
$container.addClass('comrade-nudge');
$header.append($('<strong>').text('Comrade nudge:'));
} else {
$container.addClass('comrade-info');
$header.append($('<strong>').text('Informative:'));
}
$content.append($('<span>').append(`${type.charAt(0).toUpperCase() + type.slice(1)} not listed at `, $('<a>').attr({
href: mw.util.getUrl(resolvedTitle),
target: '_blank'
}).text(resolvedTitle), "; should it be?"));
const snippet = '* {{anbl|' + currentTitle + '}}';
const $snippetWrapper = $('<div>').css({
'display': 'flex',
'align-items': 'center',
'gap': '10px',
'margin-top': '4px'
});
const $instructionSpan = $('<span>').append('If the Short description is good, consider adding ', $('<code>').text(snippet).css({
'background-color': '#f8f9fa',
'border': '1px solid #eaecf0',
'border-radius': '2px',
'padding': '1px 4px'
}));
const $copyBtn = $('<button>').text('Copy').css('margin-left', '0').click(function() {
navigator.clipboard.writeText(snippet).then(() => {
const $this = $(this);
const originalText = $this.text();
$this.text('Copied!').prop('disabled', true);
setTimeout(() => {
$this.text(originalText).prop('disabled', false);
}, 1500);
});
});
$snippetWrapper.append($instructionSpan, $copyBtn);
$content.append($snippetWrapper);
if (!hasShortDesc) {
$content.append($('<span>').css({
'color': '#d33',
'font-weight': 'bold',
'margin-top': '5px'
}).text('⚠️ Missing short description: Please add a concise one before listing.'));
}
$container.append($header, $content);
appendDismiss($container);
break;
}
}
}
}
async function createModernRedirect(redirTitle, targetTitle, rCategory, customSummary, elementId, customWikitext) {
const api = new mw.Api();
const content = customWikitext || `#REDIRECT [[${targetTitle}]]\n\n{{Redirect category shell|\n{{${rCategory}}}\n}}`;
const summary = customSummary || `Created redirect from [[${redirTitle}]]`;
return api.postWithToken('csrf', {
action: 'edit',
title: redirTitle,
text: content,
summary: summary,
createonly: true
}).done(() => {
mw.notify("Redirect created!", {
type: 'success',
tag: 'comrade',
classes: ['comrade-success']
});
$(`#${elementId}`).fadeOut();
});
}
function getSmartSortKey(fullName, entity) {
const countryIds = entity.claims?.P27?.map(c => c.mainsnak?.datavalue?.value?.id) || [];
const locationIds = entity.claims?.P17?.map(c => c.mainsnak?.datavalue?.value?.id) || [];
const placeIds = [...(entity.claims?.P19 || []), ...(entity.claims?.P119 || [])].map(c => c.mainsnak?.datavalue?.value?.id).filter(Boolean);
const allRelevantIds = [...countryIds, ...locationIds, ...placeIds];
const birthYear = parseInt(entity.claims?.P569?.[0]?.mainsnak?.datavalue?.value?.time?.match(/[+-](\d{4})/)?.[1]) || 2026;
if (fullName.startsWith("Saint ")) return fullName.replace("Saint ", "");
const arabicParticles = /\b(Abu|Abd|Abdel|Abdul|ben|bin|bint)\b/i;
if (fullName.match(arabicParticles)) {
if (birthYear > 1900) {
const parts = fullName.split(' ');
const binIndex = parts.findIndex(p => p.toLowerCase() === 'bin' || p.toLowerCase() === 'ben');
if (binIndex > 0) return parts.slice(binIndex).join(' ') + ', ' + parts.slice(0, binIndex).join(' ');
} else {
return fullName;
}
}
const eastAsianQids = ["Q148", "Q184", "Q884", "Q424", "Q881", "Q17"];
if (allRelevantIds.some(id => eastAsianQids.includes(id))) {
if (allRelevantIds.includes("Q17") && birthYear > 1885) return westernSort(fullName);
const parts = fullName.split(' ');
if (parts.length > 1) return `${parts[0]}, ${parts.slice(1).join(' ')}`;
}
return westernSort(fullName);
}
function westernSort(name) {
let cleanName = name;
if (name.startsWith("O'")) cleanName = name.replace("O'", "O");
const parts = cleanName.split(' ');
if (parts.length < 2) return cleanName;
const surname = parts.pop();
if (["Jr.", "Jr", "III", "II", "Sr.", "Sr"].includes(surname)) {
const actualSurname = parts.pop();
return `${actualSurname}, ${parts.join(' ')} ${surname}`;
}
return `${surname}, ${parts.join(' ')}`;
}
async function performDeorphan() {
const api = new mw.Api();
await api.edit(mw.config.get('wgPageName'), function(rev) {
let text = rev.content;
const summary = 'Article has backlinks; removed [[Template:Orphan|{{Orphan}}]]';
text = text.replace(/\{\{Orphan\s*(?:\|[^}]*)?\}\}\s*/gi, '');
text = text.replace(/(\{\{Multiple[ _]issues\s*)\|\s*\|/gi, '$1|');
text = replaceMI(text);
text = text.replace(/\n{3,}/g, '\n\n').trim();
return {
text,
summary,
minor: true
};
});
mw.notify('Orphan tag removed!', {
type: 'success',
tag: 'comrade',
classes: ['comrade-success']
});
setTimeout(() => location.reload(), 700);
}
function replaceMI(text) {
const miStart = /\{\{Multiple[ _]issues\s*\|/gi;
let result = '';
let lastIndex = 0;
let match;
while ((match = miStart.exec(text)) !== null) {
const start = match.index;
result += text.slice(lastIndex, start);
let depth = 0;
let i = start;
while (i < text.length) {
if (text[i] === '{' && text[i + 1] === '{') {
depth++;
i += 2;
} else if (text[i] === '}' && text[i + 1] === '}') {
depth--;
i += 2;
if (depth === 0) break;
} else {
i++;
}
}
const end = i;
const full = text.slice(start, end);
const pipeIdx = full.indexOf('|');
const inner = full.slice(pipeIdx + 1, full.length - 2).trim();
const topLevel = extractTopLevelTemplates(inner);
if (topLevel.length === 0) {
result += '';
} else if (topLevel.length === 1) {
result += topLevel[0].trim();
} else {
result += full;
}
lastIndex = end;
miStart.lastIndex = lastIndex;
}
result += text.slice(lastIndex);
return result;
}
function extractTopLevelTemplates(inner) {
const templates = [];
let i = 0;
while (i < inner.length) {
if (inner[i] === '{' && inner[i + 1] === '{') {
let depth = 0;
const start = i;
while (i < inner.length) {
if (inner[i] === '{' && inner[i + 1] === '{') {
depth++;
i += 2;
} else if (inner[i] === '}' && inner[i + 1] === '}') {
depth--;
i += 2;
if (depth === 0) break;
} else {
i++;
}
}
templates.push(inner.slice(start, i));
} else {
i++;
}
}
return templates;
}
async function performSortCorrection(newName, type) {
const api = new mw.Api();
const title = mw.config.get("wgPageName");
try {
const data = await api.get({
action: 'query',
prop: 'revisions',
titles: title,
rvprop: 'content',
formatversion: 2
});
let content = data.query.pages[0].revisions[0].content;
const dsRegex = /\{\{(?:DEFAULTSORT|Defaultsort):[^}]+\}\}/i;
const newTag = `{{DEFAULTSORT:${newName}}}`;
let actionDesc = type === "add" ? "added" : "corrected";
if (dsRegex.test(content)) {
content = content.replace(dsRegex, newTag);
} else {
const catRegex = /\[\[Category:/i;
if (catRegex.test(content)) {
content = content.replace(catRegex, `${newTag}\n[[Category:`);
} else {
content += `\n\n${newTag}`;
}
}
await api.postWithToken('csrf', {
action: 'edit',
title: title,
text: content,
summary: `Setting DEFAULTSORT to ${newName}`,
nocreate: true
});
mw.notify(`DEFAULTSORT ${actionDesc} to ${newName}!`, {
title: 'Comrade Success',
type: 'success'
});
setTimeout(() => location.reload(), 700);
} catch (err) {
mw.notify('Failed to save DEFAULTSORT.', {
type: 'error'
});
}
}
function stripDiacritics(str) {
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}
function renderSortNudge(target, reason) {
const $dsNudge = $('<div>').addClass('comrade-container comrade-nudge');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text(`Correct to: ${target}`).click(() => performSortCorrection(target, "format"));
$header.append($('<div>').append($('<strong>').text('DEFAULTSORT:'), ` ${reason} `, $btn));
$dsNudge.append($header);
appendDismiss($dsNudge);
}
async function fetchWikidataNameLabels(entity) {
const familyIds = entity.claims?.P734?.map(c => c.mainsnak.datavalue.value.id) || [];
const givenIds = entity.claims?.P735?.map(c => c.mainsnak.datavalue.value.id) || [];
const allNameIds = [...new Set([...familyIds, ...givenIds])];
if (allNameIds.length === 0) return {
givens: [],
surnames: []
};
const nameData = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetentities',
ids: allNameIds.join('|'),
props: 'labels',
languages: 'en',
format: 'json',
origin: '*'
},
dataType: 'json'
});
return {
givens: givenIds.map(id => nameData.entities[id]?.labels?.en?.value).filter(Boolean),
surnames: familyIds.map(id => nameData.entities[id]?.labels?.en?.value).filter(Boolean)
};
}
function fixNameCasing(part, referenceName) {
return part.split(' ').map(word => {
const match = referenceName.match(new RegExp(`\\b${word}\\b`, 'i'));
if (match) return match[0];
return referenceName.split(' ').find(tWord => word.toLowerCase().startsWith(tWord.toLowerCase()) || tWord.toLowerCase().startsWith(word.toLowerCase())) || word;
}).join(' ');
}
function getLongNameFromWikitext(wikitext) {
const firstLine = wikitext.split('\n').find(l => l.includes("'''"));
if (!firstLine) return null;
const boldMatch = firstLine.match(/'''(.+?)'''/);
if (!boldMatch) return null;
return boldMatch[1].replace(/\s*([“"«《„‹「『'].+?[”"»》”›」』']|\([^)\n]*\))\s*/g, ' ').replace(/\s+/g, ' ').trim();
}
if (mw.config.get("wgDBname") === "enwiki" && mw.config.get("wgUserId") === 19244234 && mw.config.get("wgNamespaceNumber") === 0 && mw.config.get("wgAction") === "view") {
$(document).ready(async function() {
const api = new mw.Api();
const qid = mw.config.get("wgWikibaseItemId");
try {
const [pageData, backlinkData] = await Promise.all([
api.get({
action: 'query',
prop: 'revisions',
titles: mw.config.get("wgPageName"),
rvprop: 'content',
formatversion: 2
}),
api.get({
action: 'query',
list: 'backlinks',
blfilterredir: 'nonredirects',
bllimit: 50,
blnamespace: 0,
bltitle: mw.config.get("wgPageName"),
formatversion: 2
})
]);
const page = pageData.query.pages[0];
if (!page || page.missing || !page.revisions) return;
const wikitext = page.revisions[0].content;
const currentTitle = mw.config.get("wgTitle");
const cleanTitle = stripDiacritics(currentTitle);
const isOrphanTagged = /\{\{\s*(Orphan|Do-attempt|Lonely|Orp|Orphaned article)/i.test(wikitext) || /\| *orphan *=/i.test(wikitext);
const backLinkCount = backlinkData.query.backlinks.length;
if (isOrphanTagged && backLinkCount >= 1) {
const $container = $('<div>').addClass('comrade-container comrade-status');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text('Remove orphan tag').click(() => performDeorphan());
$header.append($('<div>').append($('<strong>').text('Status:'), ` Article has ${backLinkCount} backlink(s).`, $btn));
$container.append($header);
appendDismiss($container);
}
if (cleanTitle !== currentTitle) checkAndRenderRedirect(cleanTitle, currentTitle, "R to diacritic", "Diacritic");
checkDomainRedirect(qid, currentTitle);
if (qid) {
const wikiData = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetentities',
ids: qid,
props: 'claims|labels',
languages: 'en',
format: 'json',
origin: '*'
},
dataType: 'json'
});
const entity = wikiData.entities[qid];
const isHuman = entity?.claims?.P31?.some(c => c.mainsnak?.datavalue?.value?.id === "Q5");
const tClean = currentTitle.replace(/\s*\(.*?\)\s*/g, '').trim();
const nameParts = tClean.split(' ');
const hasFamilyNameHatnote = /\{\{family[ _]name[ _]hatnote/i.test(wikitext);
const hasShortDesc = /\{\{\s*[Ss]hort description/i.test(wikitext);
const dsMatch = wikitext.match(/\{\{(?:DEFAULTSORT|Defaultsort):([^}]+)\}\}/i);
const isOrphan = isOrphanTagged && backLinkCount < 1;
const primarySurname = hasFamilyNameHatnote ? nameParts[0] : (getSmartSortKey(tClean, entity).split(',')[0] || nameParts[nameParts.length - 1]);
let targetSortName = "";
if (isHuman) {
const {
givens,
surnames
} = await fetchWikidataNameLabels(entity);
const refName = entity.labels?.en?.value || currentTitle;
if (dsMatch) {
const currentDS = dsMatch[1].trim();
targetSortName = currentDS;
if (currentDS.includes(',')) {
let [lastPart, firstPart] = currentDS.split(',').map(s => s.trim());
if (firstPart.toLowerCase().endsWith(lastPart.toLowerCase())) {
firstPart = firstPart.substring(0, firstPart.length - lastPart.length).trim();
targetSortName = `${fixNameCasing(lastPart, refName)}, ${fixNameCasing(firstPart, refName)}`;
renderSortNudge(targetSortName, "Redundant surname in given name field.");
} else if (givens.length > 0 && lastPart.split(' ').length > 1) {
const misplacedGiven = lastPart.split(' ').find(part => givens.some(gn => gn.toLowerCase() === part.toLowerCase()));
if (misplacedGiven) {
const newSurname = lastPart.split(' ').filter(p => p !== misplacedGiven).join(' ');
targetSortName = `${fixNameCasing(newSurname, refName)}, ${fixNameCasing(misplacedGiven + ' ' + firstPart, refName)}`;
renderSortNudge(targetSortName, `Wikidata suggests "${misplacedGiven}" is a given name.`);
}
}
}
} else {
targetSortName = hasFamilyNameHatnote ? `${nameParts[0]}, ${nameParts.slice(1).join(' ')}` : getSmartSortKey(tClean, entity);
const $dsMissing = $('<div>').addClass('comrade-container comrade-nudge');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text(`Add: {{DEFAULTSORT:${targetSortName}}}`).click(() => performSortCorrection(targetSortName, "add"));
$header.append($('<div>').append($('<strong>').text('DEFAULTSORT:'), ` Tag is missing. `, $btn));
$dsMissing.append($header);
appendDismiss($dsMissing);
}
const longName = getLongNameFromWikitext(wikitext);
if (longName && longName.length > tClean.length) {
let sortKey = hasFamilyNameHatnote ? `${longName.split(' ')[0]}, ${longName.split(' ').slice(1).join(' ')}` : getSmartSortKey(longName, entity);
const rLongWikitext = `#REDIRECT [[${currentTitle}]]\n\n{{Redirect category shell|\n{{R from long name}}\n}}\n{{DEFAULTSORT:${sortKey}}}`;
checkAndRenderRedirect(longName, currentTitle, null, "Long name", rLongWikitext);
}
if (targetSortName) {
let targetPage = currentTitle.includes(' (') ? currentTitle.split(' (')[0] : currentTitle;
let rCat = targetPage !== currentTitle ? "R from ambiguous sort name" : "R from sort name";
if (targetSortName.includes(',')) {
const p = targetSortName.split(',');
rCat += `|${p[0].trim().charAt(0).toUpperCase()}|${p[1].trim().charAt(0).toUpperCase()}`;
}
checkAndRenderRedirect(targetSortName, targetPage, rCat, "Sort name");
}
const finalFamilyNames = new Set();
const finalGivenNames = new Set();
surnames.forEach(fn => {
if (tClean.toLowerCase().includes(fn.toLowerCase())) {
fn.split(' ').forEach(part => finalFamilyNames.add(part));
finalFamilyNames.add(fn);
}
});
givens.forEach(gn => {
if (tClean.toLowerCase().includes(gn.toLowerCase())) finalGivenNames.add(gn);
});
if (finalFamilyNames.size === 0 && nameParts.length >= 2) finalFamilyNames.add(nameParts[nameParts.length - 1]);
if (finalGivenNames.size === 0 && nameParts.length >= 2) finalGivenNames.add(nameParts[0]);
const getPriorityTarget = async (name, type) => {
const titles = type === 'surname' ? [`List of people with surname ${name}`, `List of people with the English surname ${name}`, `${name} (surname)`] : [`List of people with given name ${name}`, `${name} (given name)`];
const res = await api.get({
action: 'query',
titles: titles.join('|'),
formatversion: 2
});
return titles.find(t => res.query.pages.find(pg => pg.title === t && !pg.missing));
};
for (const name of finalGivenNames) {
if (name.toLowerCase() === primarySurname.toLowerCase()) continue;
const target = await getPriorityTarget(name, 'given');
if (target) await checkNameList(name, 'given name', currentTitle, hasShortDesc, isOrphan, target);
}
for (const name of finalFamilyNames) {
const target = await getPriorityTarget(name, 'surname');
if (target) await checkNameList(name, 'surname', currentTitle, hasShortDesc, isOrphan, target);
}
}
}
} catch (err) {
console.error("Comrade error:", err);
}
});
}
})();
(function() {
if (window.IllWill) return;
const STRIKEOUT = true;
const HIDE = 'hide';
const IllWill = {
Version: "1.1.1",
Attribution: 'created by <a target="_blank" href="/wiki/User:Cobaltcigs">cobaltcigs</a>',
Summary: "Added interlanguage links using IllWill.js",
DataApi: "https://www.wikidata.org/w/api.php",
LocalLang: mw.config.get("wgPageContentLanguage"),
Disambiguator: / \(.*\)$/,
SiteLinks: {},
Config: {
MaxLanguages: 5,
IgnoreScientific: STRIKEOUT,
AutoDiff: true
},
css: `
#illwill { clear: both; margin-bottom: 1.5em; border: 1px solid #a2a9b1; padding: 15px; background: #f8f9fa; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.ill-sitelinks a { display: inline-block; margin-bottom: 3px; font-size: 0.9em; color: #36c; }
.ill-size-tag { font-size: 0.8em; color: #72777d; margin-left: 6px; white-space: nowrap; }
.illwill-ignore { text-decoration: line-through; color: #72777d; font-size: 0.85em; }
.ill-x { width: 35px; text-align: center; }
.ill-input, .ill-output { font-family: 'Courier New', monospace; white-space: pre-wrap; font-size: 0.9em; background: #fff; border: 1px solid #eaecf0; padding: 4px; border-radius: 2px; }
#ill-buttons { text-align: right; padding: 10px; border-top: 1px solid #a2a9b1; margin-top: 10px; }
#ill-buttons button { margin-left: 10px; cursor: pointer; padding: 6px 16px; border-radius: 2px; border: 1px solid #a2a9b1; transition: all 0.2s; }
#ill-ok { background: #36c; color: #fff; border-color: #36c !important; font-weight: bold; }
#ill-ok:hover { background: #447ff5; }
#ill-ok:disabled { background: #eaecf0; color: #72777d; border-color: #c8ccd1 !important; cursor: not-allowed; opacity: 0.7; }
#ill-table { width: 100%; border-collapse: collapse; margin-top: 10px; background: white; }
#ill-table th { background: #f2f2f2; text-align: left; padding: 8px; border: 1px solid #a2a9b1; }
#ill-table td { padding: 8px; border: 1px solid #a2a9b1; vertical-align: top; }
#ill-table caption { background: #eaffea; font-weight: bold; padding: 10px; border: 1px solid #a2a9b1; border-bottom: none; font-size: 1.1em; }
#ill-help { font-size: 0.85em; color: #54595d; padding: 10px; line-height: 1.4; }
.ill-loading-inline { font-style: italic; color: #72777d; font-size: 0.9em; display: block; margin: 5px 0; }
`,
setup() {
if (!["edit", "submit"].includes(mw.config.get('wgAction'))) return;
mw.loader.addStyleTag(this.css);
const link = mw.util.addPortletLink('p-cactions', '#', 'IllWill', 'ca-illwill', "Add interlanguage links via Wikidata");
if (link) {
link.addEventListener('click', (e) => {
e.preventDefault();
this.makePanel();
});
}
},
norm(s) {
if (!s) return "";
const t = s.trim().replace(/[\s_]+/g, " ");
return t.charAt(0).toUpperCase() + t.slice(1);
},
async getRedLinks($textbox) {
const txt = $textbox.textSelection('getContents') || "";
const wl = txt.match(/\[\[[^[\]\n]+\]\]/g);
if (!wl) return {
reds: [],
txt
};
const api = new mw.Api();
const textToParse = wl.join("").replace(/\|[^\]]+/g, "");
try {
const data = await api.post({
action: 'parse',
text: textToParse,
contentmodel: 'wikitext',
formatversion: 2
});
const tempDiv = document.createElement('div');
tempDiv.innerHTML = data.parse.text;
const reds = Array.from(tempDiv.querySelectorAll("a.new")).map(x => {
const urlParams = new URLSearchParams(x.href.split('?')[1]);
return urlParams.get('title')?.replace(/_/g, " ");
}).filter(Boolean);
return {
reds: [...new Set(reds)],
txt
};
} catch (e) {
return {
reds: [],
txt
};
}
},
async makePanel() {
const $editform = $('#editform');
const $textbox = $('#wpTextbox1');
$('#illwill').remove();
$editform.hide();
const $wrapper = $('<div id="illwill">Loading redlinks...</div>').insertBefore($editform);
const {
reds,
txt
} = await this.getRedLinks($textbox);
const wtLinks = [];
reds.forEach(title => {
const escaped = title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s/g, "[\\s_]+");
const re = new RegExp("\\[\\[\\s*" + escaped + "\\s*(?:\\|[^\\[\\]]+)?\\]\\]", "ig");
const match = txt.match(re);
if (match && !wtLinks.some(z => z.title === title)) {
wtLinks.push({
link: match[0],
title
});
}
});
if (!wtLinks.length) {
const $back = $('<button>Quit</button>').on('click', () => {
$wrapper.remove();
$editform.show();
});
$wrapper.empty().append($('<strong>No valid redlinks found in text.</strong> '), $back);
return;
}
const $table = $(`
<table id="ill-table" class="wikitable">
<caption>IllWill.js (v${this.Version})</caption>
<thead>
<tr>
<th class="ill-x"><input type="checkbox" id="ill-select-all" disabled></th>
<th>Input link</th>
<th>Possible Wikidata matches</th>
<th>Sitelinks (top ${this.Config.MaxLanguages} by size)</th>
<th>Resulting wikitext</th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<td colspan="3" id="ill-help">Carefully examine possible Wikidata matches.</td>
<td colspan="2" id="ill-buttons">
<button id="ill-cancel">Cancel</button>
<button id="ill-ok" disabled>Show changes</button>
</td>
</tr>
</tfoot>
</table>
`);
const $tbody = $table.find('tbody');
wtLinks.forEach((item, i) => {
$tbody.append(`
<tr id="ill-row-${i}" data-index="${i}">
<td class="ill-x"><input type="checkbox" class="ill-row-check" disabled></td>
<td class="ill-input">${this.escapeHTML(item.link)}</td>
<td class="ill-wd-item"><span class="ill-loading-inline">Searching...</span></td>
<td class="ill-sitelinks"></td>
<td class="ill-output"></td>
</tr>
`);
});
$wrapper.empty().append($table);
const chunkSize = 5;
for (let i = 0; i < wtLinks.length; i += chunkSize) {
const chunk = wtLinks.slice(i, i + chunkSize);
await Promise.all(chunk.map((item, idx) => this.fetchWikidataOptimized(item.title, i + idx)));
}
$table.on('click', '#ill-cancel', () => {
$editform.show();
$wrapper.remove();
});
$table.on('click', '#ill-ok', () => this.onOkButton($editform, $wrapper, $textbox));
$table.on('change', '#ill-select-all', (e) => {
const isChecked = $(e.target).is(':checked');
$table.find('.ill-row-check:not(:disabled)').each(function() {
$(this).prop('checked', isChecked).trigger('change');
});
});
$table.on('change', '.ill-row-check', (e) => {
this.onCheckbox(e);
this.updateOkButtonState($table);
});
$table.on('change', 'input[type="radio"]', (e) => {
this.showSiteLinks(e);
});
},
async fetchWikidataOptimized(title, index) {
const query = title.replace(this.Disambiguator, "");
const url = `${this.DataApi}?action=wbsearchentities&search=${encodeURIComponent(query)}&language=${this.LocalLang}&limit=5&format=json&origin=*`;
try {
const resp = await fetch(url);
const data = await resp.json();
const results = data.search;
const $cell = $(`#ill-row-${index} .ill-wd-item`);
if (!results || !results.length) {
$cell.html('<span class="ill-na">(no results)</span>');
return;
}
const entityIds = results.map(r => r.id);
await this.fetchEntities(entityIds, index, $cell);
} catch (e) {
$(`#ill-row-${index} .ill-wd-item`).text("Search failed.");
}
},
async fetchEntities(ids, rowIndex, $cell) {
const url = `${this.DataApi}?action=wbgetentities&ids=${ids.join('|')}&props=labels|descriptions|claims|sitelinks&format=json&origin=*`;
try {
const resp = await fetch(url);
const data = await resp.json();
$cell.empty();
ids.forEach(qNum => {
const entity = data.entities[qNum];
if (!entity) return;
const isScientific = entity.claims?.P31?.some(s => s.mainsnak?.datavalue?.value?.id === "Q13442814");
if (isScientific && this.Config.IgnoreScientific === HIDE) return;
const desc = entity.descriptions?.[this.LocalLang]?.value || entity.descriptions?.en?.value || "";
const label = entity.labels?.[this.LocalLang]?.value || entity.labels?.en?.value || qNum;
const isStrikeout = isScientific && this.Config.IgnoreScientific === STRIKEOUT;
const $div = $(`
<div class="${isStrikeout ? 'illwill-ignore' : ''}">
<input type="radio" name="selection-${rowIndex}" value="${qNum}" id="radio-${qNum}" ${isStrikeout ? 'disabled' : ''}>
<label for="radio-${qNum}">
<a href="https://wikidata.org/wiki/${qNum}" target="_blank">${this.escapeHTML(label)}</a>:
<span class="ill-desc">${desc ? this.escapeHTML(desc) : '<i>no description</i>'}</span>
</label>
</div>
`);
$cell.append($div);
this.SiteLinks[qNum] = entity.sitelinks || {};
});
} catch (e) {
$cell.text("Detail fetch failed.");
}
},
updateOkButtonState($table) {
const $rows = $table.find('.ill-row-check:not(:disabled)');
const $checkedRows = $rows.filter(':checked');
const anyEnabled = $rows.length > 0;
$table.find('#ill-ok').prop('disabled', $checkedRows.length === 0);
const $master = $table.find('#ill-select-all');
$master.prop('disabled', !anyEnabled);
if (anyEnabled && $rows.length === $checkedRows.length) {
$master.prop('checked', true);
} else {
$master.prop('checked', false);
}
},
async showSiteLinks(e) {
const isManualToggling = e.fromCheckbox || false;
const qNum = e.target.value;
const $row = $(e.target).closest('tr');
const sitelinks = this.SiteLinks[qNum];
const $checkbox = $row.find('.ill-row-check');
const $sitelinkCell = $row.find('.ill-sitelinks');
const $outputCell = $row.find('.ill-output');
const $table = $('#ill-table');
const effectiveMax = Math.min(Math.max(this.Config.MaxLanguages, 3), 6);
const validKeys = Object.keys(sitelinks).filter(k => k.endsWith('wiki') && !k.includes('commons'));
$sitelinkCell.html('<span class="ill-loading-inline">Measuring articles...</span>');
$outputCell.empty();
if (!isManualToggling) {
$checkbox.prop('disabled', true).prop('checked', false);
}
const candidates = validKeys.slice(0, 25);
const sizeMap = [];
await Promise.all(candidates.map(async (key) => {
const lang = key.replace('wiki', '').replace(/_/g, '-');
const title = sitelinks[key].title;
const endpoint = `https://${lang}.wikipedia.org/w/api.php?action=query&titles=${encodeURIComponent(title)}&prop=info&format=json&origin=*`;
try {
const res = await fetch(endpoint).then(r => r.json());
const page = Object.values(res.query.pages)[0];
sizeMap.push({
lang,
title,
length: page.length || 0
});
} catch (err) {
sizeMap.push({
lang,
title,
length: 0
});
}
}));
sizeMap.sort((a, b) => b.length - a.length);
const topLinks = sizeMap.slice(0, effectiveMax);
$sitelinkCell.html(topLinks.map(item => {
const kbValue = Math.round(item.length / 1024);
return `<div><a href="https://${item.lang}.wikipedia.org/wiki/${encodeURIComponent(item.title)}" target="_blank">${item.lang}:${this.escapeHTML(item.title)}</a> <span class="ill-size-tag">(${kbValue} kB)</span></div>`;
}).join(""));
const inputWikitext = $row.find('.ill-input').text();
const match = inputWikitext.match(/\[\[(.*?)\]\]/);
if (match) {
const parts = match[1].split('|');
const targetPage = parts[0].trim();
const surfaceName = (parts[1] || parts[0]).trim();
const illLangs = topLinks.map(item => {
const isSameTitle = this.norm(item.title) === this.norm(targetPage);
return `|${item.lang}|${isSameTitle ? '' : item.title}`;
}).join("");
const ltParam = (this.norm(surfaceName) === this.norm(targetPage)) ? "" : `|lt=${surfaceName}`;
$outputCell.text(`{{ill|${targetPage}${ltParam}${illLangs}}}`);
}
$checkbox.prop('disabled', !topLinks.length);
if (!isManualToggling) {
$checkbox.prop('checked', !!topLinks.length);
}
this.updateOkButtonState($table);
},
onCheckbox(e) {
const $row = $(e.target).closest('tr');
const $radio = $row.find('input[type="radio"]:checked');
const isChecked = $(e.target).is(':checked');
if (isChecked) {
if ($radio.length) {
this.showSiteLinks({
target: $radio[0],
fromCheckbox: true
});
}
} else {
$row.find('.ill-sitelinks, .ill-output').empty();
}
},
onOkButton($editform, $wrapper, $textbox) {
let text = $textbox.textSelection('getContents');
let changed = false;
$wrapper.find('.ill-row-check:checked').each(function() {
const $row = $(this).closest('tr');
const input = $row.find('.ill-input').text();
const output = $row.find('.ill-output').text();
if (output) {
const escapedInput = input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
text = text.replace(new RegExp(escapedInput, "gi"), output);
changed = true;
}
});
if (changed) {
$('#wpSummary').val((i, v) => (v ? v + "; " : "") + this.Summary);
$textbox.textSelection('setContents', text);
$wrapper.remove();
$editform.show();
if (this.Config.AutoDiff) $('#wpDiff').click();
}
},
escapeHTML(str) {
return str.replace(/[&<>"']/g, m => ({
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
})[m]);
}
};
window.IllWill = IllWill;
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'jquery.textSelection'], () => {
$(() => IllWill.setup());
});
})();
//</nowiki>
47615lh2wxm2gsj6moulss9595uhub1
739071
739070
2026-04-22T06:19:55Z
Sam Sailor
26820
Test
739071
javascript
text/javascript
//<nowiki>
window.headmaster_auto_run = true;
(function() {
if (window.Headmaster) return;
const defaultNS = [0, 2, 10, 118];
const userNS = window.headmaster_ns || [];
const config = {
menu: window.headmaster_menu || 'p-cactions',
enableSummary: window.headmaster_do_summary !== false,
customSummary: window.headmaster_summary || "",
allowedNamespaces: Array.from(new Set([...defaultNS, ...userNS])),
autoScan: window.headmaster_auto_scan !== false,
autoRun: window.headmaster_auto_run === true
};
const Headmaster = {
VERSION: "0.9.1",
DefaultSummary: "Converting [[MOS:PSEUDOHEAD|pseudo-headings]] {details}using [[User:Sam_Sailor/Scripts/Headmaster.js|Headmaster]]. Semicolon markup is reserved for [[MOS:DLIST|description lists]].",
styleInjected: false,
css: `
#hm-panel { clear: both; border: 1px solid #a2a9b1; padding: 15px; background: #f8f9fa; margin-bottom: 1.5em; border-radius: 2px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
#hm-table { width: 100%; border-collapse: collapse; background: white; margin-top: 10px; }
#hm-table th { background: #f2f2f2; border: 1px solid #a2a9b1; padding: 8px; text-align: left; }
#hm-table td { border: 1px solid #a2a9b1; padding: 8px; vertical-align: middle; }
.hm-context { font-family: 'Courier New', monospace; font-size: 0.9em; color: #54595d; background: #fdfdfd; }
#hm-controls { display: flex; justify-content: space-between; align-items: center; margin-top: 15px; }
#hm-controls button { margin-left: 10px; padding: 6px 16px; cursor: pointer; border-radius: 2px; }
.hm-btn-apply { background: #36c; color: white; border: 1px solid #36c; font-weight: bold; }
.hm-btn-apply:hover { background: #447ff5; }
.hm-global-selector { font-size: 0.9em; color: #202122; background: #eaecf0; padding: 8px; border-radius: 2px; border: 1px solid #a2a9b1; }
.hm-global-selector a { font-weight: bold; text-decoration: none; color: #36c; }
.hm-global-selector a:hover { text-decoration: underline; }
.hm-locate-btn { cursor: pointer; color: #36c; border: 1px solid #a2a9b1; background: #f8f9fa; padding: 2px 6px; font-size: 0.85em; border-radius: 2px; }
.hm-locate-btn:hover { background: #fff; border-color: #36c; }
`,
setup() {
if (!config.allowedNamespaces.includes(mw.config.get("wgNamespaceNumber"))) return;
const action = mw.config.get("wgAction");
this.addPortlet(action);
if (action === 'view' && (config.autoScan || config.autoRun)) {
this.silentScan();
}
if (mw.util.getParamValue('headmaster') === '1' && ['edit', 'submit'].includes(action)) {
const params = new URLSearchParams(window.location.search);
params.delete('headmaster');
const searchString = params.toString();
const newUrl = window.location.pathname + (searchString ? '?' + searchString : '');
window.history.replaceState(null, '', newUrl);
this.init();
}
},
addPortlet(action) {
const link = mw.util.addPortletLink(config.menu, '#', 'Headmaster', 'ca-hm', 'Interactively convert pseudo-headings');
if (link) {
$(link).on('click', (e) => {
e.preventDefault();
if (action === 'view') {
this.redirectToEdit();
} else if (['edit', 'submit'].includes(action)) {
this.init();
}
});
}
},
redirectToEdit() {
window.location.href = mw.util.getUrl(mw.config.get("wgPageName"), {
action: 'edit',
headmaster: '1'
});
},
silentScan() {
new mw.Api().get({
action: 'query',
prop: 'revisions',
titles: mw.config.get('wgPageName'),
rvprop: 'content',
rvlimit: 1,
formatversion: 2
}).done((data) => {
const page = data.query.pages[0];
if (!page || !page.revisions) return;
const wikitext = page.revisions[0].content;
const tasks = this.analyzeText(wikitext);
if (tasks.length > 0) {
if (config.autoRun) {
this.redirectToEdit();
} else {
this.notifyDiscovery(tasks.length);
}
}
});
},
notifyDiscovery(count) {
const $msg = $('<span>').text(`Headmaster: Found ${count} pseudo-headings. `).append($('<a>').text('Run conversion').css({
fontWeight: 'bold',
cursor: 'pointer'
}).on('click', () => this.redirectToEdit()));
mw.notify($msg, {
tag: 'hm-discovery',
autoHide: false
});
},
init() {
if (!this.styleInjected) {
mw.loader.addStyleTag(this.css);
this.styleInjected = true;
}
const $textbox = $('#wpTextbox1');
const wikitext = $textbox.textSelection('getContents');
if (!wikitext) {
mw.notify("Textbox is empty or not found.", {
type: 'error'
});
return;
}
const tasks = this.analyzeText(wikitext);
if (tasks.length === 0) {
mw.notify("No pseudo-headings found.");
return;
}
this.showUI(tasks, wikitext);
},
analyzeText(wikitext) {
const lines = wikitext.split("\n");
const tasks = [];
let currentDepth = 2;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
const headingMatch = line.match(/^(={2,6})\s*.+?\s*\1\s*$/);
if (headingMatch) currentDepth = headingMatch[1].length;
if (line.startsWith(';') && !line.startsWith(';;')) {
const nextLine = lines[i + 1] || "";
if (!nextLine.trim().startsWith(':')) {
const cleanText = lines[i].substring(1).replace(/\s*:.*$/, "").trim();
const suggestedLevel = "=".repeat(Math.min(currentDepth + 1, 6));
tasks.push({
index: i,
original: lines[i],
cleanText: cleanText,
context: nextLine.substring(0, 60).trim() + (nextLine.length > 60 ? "..." : ""),
suggestedLevel: suggestedLevel
});
}
}
}
return tasks;
},
showUI(tasks, originalWikitext) {
$('#hm-panel').remove();
$('#editform').hide();
const tableRows = tasks.map((task, i) => `
<tr data-task-index="${task.index}">
<td><button class="hm-locate-btn" data-hm-i="${i}" title="Show in editor">Locate</button></td>
<td class="hm-context"><code>${mw.html.escape(task.original)}</code></td>
<td><label><input type="radio" name="hm-opt-${i}" value="keep"> Keep</label></td>
<td><label><input type="radio" name="hm-opt-${i}" value="bold" checked> <b>Bold</b></label></td>
<td><label><input type="radio" name="hm-opt-${i}" value="head"> Heading (${task.suggestedLevel.length})</label></td>
<td class="hm-context">${mw.html.escape(task.context || "(end of page)")}</td>
</tr>
`).join("");
const $panel = $(`
<div id="hm-panel">
<h3 style="margin-top:0">Headmaster v${this.VERSION}: Review pseudo-headings</h3>
<div class="hm-global-selector">
<strong>Bulk action:</strong>
<a href="#" id="hm-all-keep">Set all to Keep</a> |
<a href="#" id="hm-all-bold">Set all to Bold</a> |
<a href="#" id="hm-all-head">Set all to Heading</a>
</div>
<table id="hm-table">
<thead>
<tr>
<th>Find</th>
<th>Source</th>
<th colspan="3">Action</th>
<th>Next line context</th>
</tr>
</thead>
<tbody>${tableRows}</tbody>
</table>
<div id="hm-controls">
<span>Found <strong>${tasks.length}</strong> possible pseudo-headings.</span>
<div>
<button id="hm-cancel" class="mw-ui-button">Cancel</button>
<button id="hm-apply" class="hm-btn-apply">Show changes (diff)</button>
</div>
</div>
</div>
`);
$('#content').prepend($panel);
window.scrollTo(0, 0);
$('.hm-locate-btn').on('click', function() {
$(document).off('mousedown.hmlocate');
const idx = $(this).data('hm-i');
const task = tasks[idx];
const lines = originalWikitext.split('\n');
let charOffset = 0;
for (let j = 0; j < task.index; j++) {
charOffset += lines[j].length + 1;
}
$('#hm-panel').hide();
$('#editform').show();
const $textbox = $('#wpTextbox1');
$textbox.textSelection('setSelection', {
start: charOffset,
end: charOffset + lines[task.index].length
});
$textbox.textSelection('scrollToCaretPosition');
const locateNotify = mw.notify("Locating line " + (task.index + 1) + ". Click outside the editor to return.", {
tag: 'hm-locate',
type: 'success',
autoHide: false
});
setTimeout(() => {
$(document).on('mousedown.hmlocate', function(e) {
if (!$(e.target).closest('#editform, .mw-notification-area').length) {
$(document).off('mousedown.hmlocate');
locateNotify.then(n => n.close());
$('#editform').hide();
$('#hm-panel').show();
}
});
}, 200);
});
$('#hm-all-keep').on('click', (e) => {
e.preventDefault();
$('input[value="keep"]').prop('checked', true);
});
$('#hm-all-bold').on('click', (e) => {
e.preventDefault();
$('input[value="bold"]').prop('checked', true);
});
$('#hm-all-head').on('click', (e) => {
e.preventDefault();
$('input[value="head"]').prop('checked', true);
});
$('#hm-cancel').on('click', () => {
$('#hm-panel').remove();
$('#editform').show();
});
$('#hm-apply').on('click', () => this.applyChanges(tasks, originalWikitext));
},
getProcessedSummary(existingSummary, bolds, heads) {
if (!config.enableSummary) return existingSummary;
let mySummary, isDefault = false;
if (config.customSummary) {
mySummary = config.customSummary;
} else {
isDefault = true;
const detailsArr = [];
if (bolds > 0) detailsArr.push(`${bolds} to bold`);
if (heads > 0) detailsArr.push(`${heads} to heading`);
const detailsStr = detailsArr.length > 0 ? `(${detailsArr.join(', ')}) ` : "";
mySummary = this.DefaultSummary.replace("{details}", detailsStr);
}
const trimmedOld = existingSummary.trim();
if (!trimmedOld) return mySummary;
if (/[.!?]$/.test(trimmedOld)) {
return trimmedOld + " " + mySummary;
} else {
const finalSummary = isDefault ? mySummary.charAt(0).toLowerCase() + mySummary.slice(1) : mySummary;
return trimmedOld + "; " + finalSummary;
}
},
applyChanges(tasks, wikitext) {
try {
const lines = wikitext.split("\n");
let bCount = 0,
hCount = 0;
$('#hm-table tbody tr').each(function(i) {
const action = $(this).find('input:checked').val();
const task = tasks[i];
if (action === 'bold') {
lines[task.index] = `'''${task.cleanText}'''`;
bCount++;
} else if (action === 'head') {
const h = task.suggestedLevel;
lines[task.index] = `${h} ${task.cleanText} ${h}`;
hCount++;
}
});
if (bCount + hCount === 0) {
mw.notify("No changes selected.");
$('#hm-panel').remove();
$('#editform').show();
return;
}
const $textbox = $('#wpTextbox1');
const $summary = $('#wpSummary');
$textbox.textSelection('setContents', lines.join("\n"));
$summary.val(this.getProcessedSummary($summary.val(), bCount, hCount));
$('#hm-panel').remove();
$('#editform').show();
$('#wpDiff').click();
} catch (err) {
$('#editform').show();
mw.notify("Headmaster error: " + err.message, {
type: 'error'
});
}
}
};
window.Headmaster = Headmaster;
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'jquery.textSelection'], () => {
$(() => Headmaster.setup());
});
})();
(function() {
var Comrade = Comrade || {};
mw.util.addCSS(`
.ambox-Orphan { display: block !important; }
.comrade-container {
box-shadow: 0 1px 3px rgba(0,0,0,0.1); border-radius: 2px;
padding: 10px 15px; margin: 10px 0; display: flex;
flex-direction: column; align-items: flex-start; gap: 8px;
border: 1px solid #a2a9b1; background: #fcfcfc; line-height: 1.5;
}
.comrade-header { display: flex; align-items: center; justify-content: space-between; width: 100%; }
.comrade-content { display: flex; flex-direction: column; gap: 5px; width: 100%; }
.comrade-success { background-color: #d5f5e3 !important; border-left: 5px solid #27ae60 !important; color: #1b5e20 !important; }
.comrade-nudge { background: #fff9ea; border-left: 5px solid #f0ad4e; }
.comrade-info { background: #eaf3ff; border-left: 5px solid #36c; }
.comrade-status { background: #eaf3ff; border-left: 5px solid #36c; }
.comrade-container button:not(.comrade-dismiss) {
background: #f8f9fa; border: 1px solid #a2a9b1; border-radius: 2px;
padding: 2px 10px; cursor: pointer; font-weight: bold; font-size: 0.95em; margin-left: 10px;
}
.comrade-container button:not(.comrade-dismiss):hover { border-color: #36c; color: #36c; background: #fff; }
.comrade-dismiss {
background: transparent; border: none; color: #d33; font-weight: bold;
cursor: pointer; font-size: 0.9em;
}
.comrade-dismiss:hover { text-decoration: underline; }
.comrade-code-block { background: #f1f1f1; padding: 4px 8px; border-radius: 3px; font-family: monospace; display: inline-block; margin-top: 4px; }
`);
function appendDismiss($el) {
$('<button>').addClass('comrade-dismiss').text('Dismiss').click(function() {
$(this).closest('.comrade-container').fadeOut();
}).appendTo($el.find('.comrade-header'));
$("#siteSub").after($el);
}
async function checkAndRenderRedirect(redirTitle, targetTitle, rCategory, labelText, customWikitext) {
const api = new mw.Api();
const res = await api.get({
action: 'query',
titles: redirTitle,
formatversion: 2
});
if (res.query.pages[0].missing) {
const safeId = "comrade-redir-" + btoa(unescape(encodeURIComponent(redirTitle))).replace(/=/g, "");
const $container = $('<div>').addClass('comrade-container comrade-info').attr('id', safeId);
const $header = $('<div>').addClass('comrade-header');
const summary = `Created redirect from ${labelText.toLowerCase()} to [[${targetTitle}]]`;
const $btn = $('<button>').text('Create redirect').click(() => createModernRedirect(redirTitle, targetTitle, rCategory, summary, safeId, customWikitext));
$header.append($('<div>').append($('<strong>').text(`${labelText}: `), `Create redirect from `, $('<code>').text(redirTitle), $btn));
$container.append($header);
appendDismiss($container);
}
}
async function checkDomainRedirect(qid, currentTitle) {
let domainCandidate = "";
if (qid) {
try {
const res = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetclaims',
entity: qid,
property: 'P856',
format: 'json',
origin: '*'
}
});
if (res.claims?.P856) {
domainCandidate = res.claims.P856[0].mainsnak.datavalue.value;
}
} catch (e) {
console.log("Comrade: Wikidata API call failed.");
}
}
if (!domainCandidate) {
domainCandidate = $(".infobox .url a").last().attr("href") || $(".official-website a").first().attr("href");
}
if (domainCandidate) {
try {
const url = new URL(domainCandidate);
let domain = url.hostname.replace(/^www\d*\./, "");
if (domain.includes(".") && url.pathname === "/") {
const citationURL = '/api/rest_v1/data/citation/mediawiki/' + encodeURIComponent(domainCandidate);
$.ajax({
url: citationURL,
method: 'GET',
success: function() {
checkAndRenderRedirect(domain, currentTitle, "R from domain name", "Domain");
}
});
}
} catch (e) {
console.log("Comrade: Error parsing URL object.");
}
}
}
async function checkNameList(name, type, currentTitle, hasShortDesc, isOrphan) {
const api = new mw.Api();
const candidates = [`${name} (${type})`, `${name} (${type.toLowerCase()})`, `${name} (name)`, name];
const checked = new Set();
for (const title of candidates) {
if (checked.has(title)) continue;
checked.add(title);
const res = await api.get({
action: 'query',
prop: 'revisions',
titles: title,
rvprop: 'content',
formatversion: 2,
redirects: 0
});
const page = res.query.pages[0];
if (page && !page.missing) {
const text = page.revisions[0].content;
const resolvedTitle = page.title;
const isNameMatch = /\{\{(Surname|Hndis|Given[ _]name|Forename|Givenname)/i.test(text);
const isNameDab = /\{\{(Disambiguation|Dab|Disambig)(?:[^}]*\|(surname|surnames|given[ _]name|given[ _]names)|(?:\s*\}\}))/i.test(text);
if (isNameMatch || isNameDab) {
const escapedTitle = currentTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/ /g, '[ _]');
const alreadyListed = new RegExp('(\\[\\[|\\{\\{anbl\\|[ _]*|\\|)' + escapedTitle, 'i').test(text);
if (alreadyListed) break;
const $container = $('<div>').addClass('comrade-container');
const $header = $('<div>').addClass('comrade-header');
const $content = $('<div>').addClass('comrade-content');
if (isOrphan) {
$container.addClass('comrade-nudge');
$header.append($('<strong>').text('Comrade nudge:'));
} else {
$container.addClass('comrade-info');
$header.append($('<strong>').text('Informative:'));
}
$content.append($('<span>').append(`${type.charAt(0).toUpperCase() + type.slice(1)} not listed at `, $('<a>').attr({
href: mw.util.getUrl(resolvedTitle),
target: '_blank'
}).text(resolvedTitle), "; should it be?"));
const snippet = '* {{anbl|' + currentTitle + '}}';
const $snippetWrapper = $('<div>').css({
'display': 'flex',
'align-items': 'center',
'gap': '10px',
'margin-top': '4px'
});
const $instructionSpan = $('<span>').append('If the Short description is good, consider adding ', $('<code>').text(snippet).css({
'background-color': '#f8f9fa',
'border': '1px solid #eaecf0',
'border-radius': '2px',
'padding': '1px 4px'
}));
const $copyBtn = $('<button>').text('Copy').css('margin-left', '0').click(function() {
navigator.clipboard.writeText(snippet).then(() => {
const $this = $(this);
const originalText = $this.text();
$this.text('Copied!').prop('disabled', true);
setTimeout(() => {
$this.text(originalText).prop('disabled', false);
}, 1500);
});
});
$snippetWrapper.append($instructionSpan, $copyBtn);
$content.append($snippetWrapper);
if (!hasShortDesc) {
$content.append($('<span>').css({
'color': '#d33',
'font-weight': 'bold',
'margin-top': '5px'
}).text('⚠️ Missing short description: Please add a concise one before listing.'));
}
$container.append($header, $content);
appendDismiss($container);
break;
}
}
}
}
async function createModernRedirect(redirTitle, targetTitle, rCategory, customSummary, elementId, customWikitext) {
const api = new mw.Api();
const content = customWikitext || `#REDIRECT [[${targetTitle}]]\n\n{{Redirect category shell|\n{{${rCategory}}}\n}}`;
const summary = customSummary || `Created redirect from [[${redirTitle}]]`;
return api.postWithToken('csrf', {
action: 'edit',
title: redirTitle,
text: content,
summary: summary,
createonly: true
}).done(() => {
mw.notify("Redirect created!", {
type: 'success',
tag: 'comrade',
classes: ['comrade-success']
});
$(`#${elementId}`).fadeOut();
});
}
function getSmartSortKey(fullName, entity) {
const countryIds = entity.claims?.P27?.map(c => c.mainsnak?.datavalue?.value?.id) || [];
const locationIds = entity.claims?.P17?.map(c => c.mainsnak?.datavalue?.value?.id) || [];
const placeIds = [...(entity.claims?.P19 || []), ...(entity.claims?.P119 || [])].map(c => c.mainsnak?.datavalue?.value?.id).filter(Boolean);
const allRelevantIds = [...countryIds, ...locationIds, ...placeIds];
const birthYear = parseInt(entity.claims?.P569?.[0]?.mainsnak?.datavalue?.value?.time?.match(/[+-](\d{4})/)?.[1]) || 2026;
if (fullName.startsWith("Saint ")) return fullName.replace("Saint ", "");
const arabicParticles = /\b(Abu|Abd|Abdel|Abdul|ben|bin|bint)\b/i;
if (fullName.match(arabicParticles)) {
if (birthYear > 1900) {
const parts = fullName.split(' ');
const binIndex = parts.findIndex(p => p.toLowerCase() === 'bin' || p.toLowerCase() === 'ben');
if (binIndex > 0) return parts.slice(binIndex).join(' ') + ', ' + parts.slice(0, binIndex).join(' ');
} else {
return fullName;
}
}
const eastAsianQids = ["Q148", "Q184", "Q884", "Q424", "Q881", "Q17"];
if (allRelevantIds.some(id => eastAsianQids.includes(id))) {
if (allRelevantIds.includes("Q17") && birthYear > 1885) return westernSort(fullName);
const parts = fullName.split(' ');
if (parts.length > 1) return `${parts[0]}, ${parts.slice(1).join(' ')}`;
}
return westernSort(fullName);
}
function westernSort(name) {
let cleanName = name;
if (name.startsWith("O'")) cleanName = name.replace("O'", "O");
const parts = cleanName.split(' ');
if (parts.length < 2) return cleanName;
const surname = parts.pop();
if (["Jr.", "Jr", "III", "II", "Sr.", "Sr"].includes(surname)) {
const actualSurname = parts.pop();
return `${actualSurname}, ${parts.join(' ')} ${surname}`;
}
return `${surname}, ${parts.join(' ')}`;
}
async function performDeorphan() {
const api = new mw.Api();
await api.edit(mw.config.get('wgPageName'), function(rev) {
let text = rev.content;
const summary = 'Article has backlinks; removed [[Template:Orphan|{{Orphan}}]]';
text = text.replace(/\{\{Orphan\s*(?:\|[^}]*)?\}\}\s*/gi, '');
text = text.replace(/(\{\{Multiple[ _]issues\s*)\|\s*\|/gi, '$1|');
text = replaceMI(text);
text = text.replace(/\n{3,}/g, '\n\n').trim();
return {
text,
summary,
minor: true
};
});
mw.notify('Orphan tag removed!', {
type: 'success',
tag: 'comrade',
classes: ['comrade-success']
});
setTimeout(() => location.reload(), 700);
}
function replaceMI(text) {
const miStart = /\{\{Multiple[ _]issues\s*\|/gi;
let result = '';
let lastIndex = 0;
let match;
while ((match = miStart.exec(text)) !== null) {
const start = match.index;
result += text.slice(lastIndex, start);
let depth = 0;
let i = start;
while (i < text.length) {
if (text[i] === '{' && text[i + 1] === '{') {
depth++;
i += 2;
} else if (text[i] === '}' && text[i + 1] === '}') {
depth--;
i += 2;
if (depth === 0) break;
} else {
i++;
}
}
const end = i;
const full = text.slice(start, end);
const pipeIdx = full.indexOf('|');
const inner = full.slice(pipeIdx + 1, full.length - 2).trim();
const topLevel = extractTopLevelTemplates(inner);
if (topLevel.length === 0) {
result += '';
} else if (topLevel.length === 1) {
result += topLevel[0].trim();
} else {
result += full;
}
lastIndex = end;
miStart.lastIndex = lastIndex;
}
result += text.slice(lastIndex);
return result;
}
function extractTopLevelTemplates(inner) {
const templates = [];
let i = 0;
while (i < inner.length) {
if (inner[i] === '{' && inner[i + 1] === '{') {
let depth = 0;
const start = i;
while (i < inner.length) {
if (inner[i] === '{' && inner[i + 1] === '{') {
depth++;
i += 2;
} else if (inner[i] === '}' && inner[i + 1] === '}') {
depth--;
i += 2;
if (depth === 0) break;
} else {
i++;
}
}
templates.push(inner.slice(start, i));
} else {
i++;
}
}
return templates;
}
async function performSortCorrection(newName, type) {
const api = new mw.Api();
const title = mw.config.get("wgPageName");
try {
const data = await api.get({
action: 'query',
prop: 'revisions',
titles: title,
rvprop: 'content',
formatversion: 2
});
let content = data.query.pages[0].revisions[0].content;
const dsRegex = /\{\{(?:DEFAULTSORT|Defaultsort):[^}]+\}\}/i;
const newTag = `{{DEFAULTSORT:${newName}}}`;
let actionDesc = type === "add" ? "added" : "corrected";
if (dsRegex.test(content)) {
content = content.replace(dsRegex, newTag);
} else {
const catRegex = /\[\[Category:/i;
if (catRegex.test(content)) {
content = content.replace(catRegex, `${newTag}\n[[Category:`);
} else {
content += `\n\n${newTag}`;
}
}
await api.postWithToken('csrf', {
action: 'edit',
title: title,
text: content,
summary: `Setting DEFAULTSORT to ${newName}`,
nocreate: true
});
mw.notify(`DEFAULTSORT ${actionDesc} to ${newName}!`, {
title: 'Comrade Success',
type: 'success'
});
setTimeout(() => location.reload(), 700);
} catch (err) {
mw.notify('Failed to save DEFAULTSORT.', {
type: 'error'
});
}
}
function stripDiacritics(str) {
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}
function renderSortNudge(target, reason) {
const $dsNudge = $('<div>').addClass('comrade-container comrade-nudge');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text(`Correct to: ${target}`).click(() => performSortCorrection(target, "format"));
$header.append($('<div>').append($('<strong>').text('DEFAULTSORT:'), ` ${reason} `, $btn));
$dsNudge.append($header);
appendDismiss($dsNudge);
}
async function fetchWikidataNameLabels(entity) {
const familyIds = entity.claims?.P734?.map(c => c.mainsnak.datavalue.value.id) || [];
const givenIds = entity.claims?.P735?.map(c => c.mainsnak.datavalue.value.id) || [];
const allNameIds = [...new Set([...familyIds, ...givenIds])];
if (allNameIds.length === 0) return {
givens: [],
surnames: []
};
const nameData = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetentities',
ids: allNameIds.join('|'),
props: 'labels',
languages: 'en',
format: 'json',
origin: '*'
},
dataType: 'json'
});
return {
givens: givenIds.map(id => nameData.entities[id]?.labels?.en?.value).filter(Boolean),
surnames: familyIds.map(id => nameData.entities[id]?.labels?.en?.value).filter(Boolean)
};
}
function fixNameCasing(part, referenceName) {
return part.split(' ').map(word => {
const match = referenceName.match(new RegExp(`\\b${word}\\b`, 'i'));
if (match) return match[0];
return referenceName.split(' ').find(tWord => word.toLowerCase().startsWith(tWord.toLowerCase()) || tWord.toLowerCase().startsWith(word.toLowerCase())) || word;
}).join(' ');
}
function getLongNameFromWikitext(wikitext) {
const firstLine = wikitext.split('\n').find(l => l.includes("'''"));
if (!firstLine) return null;
const boldMatch = firstLine.match(/'''(.+?)'''/);
if (!boldMatch) return null;
return boldMatch[1].replace(/\s*([“"«《„‹「『'].+?[”"»》”›」』']|\([^)\n]*\))\s*/g, ' ').replace(/\s+/g, ' ').trim();
}
if (mw.config.get("wgDBname") === "enwiki" && mw.config.get("wgUserId") === 19244234 && mw.config.get("wgNamespaceNumber") === 0 && mw.config.get("wgAction") === "view") {
$(document).ready(async function() {
const api = new mw.Api();
const qid = mw.config.get("wgWikibaseItemId");
try {
const [pageData, backlinkData] = await Promise.all([
api.get({
action: 'query',
prop: 'revisions',
titles: mw.config.get("wgPageName"),
rvprop: 'content',
formatversion: 2
}),
api.get({
action: 'query',
list: 'backlinks',
blfilterredir: 'nonredirects',
bllimit: 50,
blnamespace: 0,
bltitle: mw.config.get("wgPageName"),
formatversion: 2
})
]);
const page = pageData.query.pages[0];
if (!page || page.missing || !page.revisions) return;
const wikitext = page.revisions[0].content;
const currentTitle = mw.config.get("wgTitle");
const cleanTitle = stripDiacritics(currentTitle);
const isOrphanTagged = /\{\{\s*(Orphan|Do-attempt|Lonely|Orp|Orphaned article)/i.test(wikitext) || /\| *orphan *=/i.test(wikitext);
const backLinkCount = backlinkData.query.backlinks.length;
if (isOrphanTagged && backLinkCount >= 1) {
const $container = $('<div>').addClass('comrade-container comrade-status');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text('Remove orphan tag').click(() => performDeorphan());
$header.append($('<div>').append($('<strong>').text('Status:'), ` Article has ${backLinkCount} backlink(s).`, $btn));
$container.append($header);
appendDismiss($container);
}
if (cleanTitle !== currentTitle) checkAndRenderRedirect(cleanTitle, currentTitle, "R to diacritic", "Diacritic");
checkDomainRedirect(qid, currentTitle);
if (qid) {
const wikiData = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetentities',
ids: qid,
props: 'claims|labels',
languages: 'en',
format: 'json',
origin: '*'
},
dataType: 'json'
});
const entity = wikiData.entities[qid];
const isHuman = entity?.claims?.P31?.some(c => c.mainsnak?.datavalue?.value?.id === "Q5");
const tClean = currentTitle.replace(/\s*\(.*?\)\s*/g, '').trim();
const nameParts = tClean.split(' ');
const hasFamilyNameHatnote = /\{\{family[ _]name[ _]hatnote/i.test(wikitext);
const hasShortDesc = /\{\{\s*[Ss]hort description/i.test(wikitext);
const dsMatch = wikitext.match(/\{\{(?:DEFAULTSORT|Defaultsort):([^}]+)\}\}/i);
const isOrphan = isOrphanTagged && backLinkCount < 1;
const primarySurname = hasFamilyNameHatnote ? nameParts[0] : (getSmartSortKey(tClean, entity).split(',')[0] || nameParts[nameParts.length - 1]);
let targetSortName = "";
if (isHuman) {
const {
givens,
surnames
} = await fetchWikidataNameLabels(entity);
const refName = entity.labels?.en?.value || currentTitle;
if (dsMatch) {
const currentDS = dsMatch[1].trim();
targetSortName = currentDS;
if (currentDS.includes(',')) {
let [lastPart, firstPart] = currentDS.split(',').map(s => s.trim());
if (firstPart.toLowerCase().endsWith(lastPart.toLowerCase())) {
firstPart = firstPart.substring(0, firstPart.length - lastPart.length).trim();
targetSortName = `${fixNameCasing(lastPart, refName)}, ${fixNameCasing(firstPart, refName)}`;
renderSortNudge(targetSortName, "Redundant surname in given name field.");
} else if (givens.length > 0 && lastPart.split(' ').length > 1) {
const misplacedGiven = lastPart.split(' ').find(part => givens.some(gn => gn.toLowerCase() === part.toLowerCase()));
if (misplacedGiven) {
const newSurname = lastPart.split(' ').filter(p => p !== misplacedGiven).join(' ');
targetSortName = `${fixNameCasing(newSurname, refName)}, ${fixNameCasing(misplacedGiven + ' ' + firstPart, refName)}`;
renderSortNudge(targetSortName, `Wikidata suggests "${misplacedGiven}" is a given name.`);
}
}
}
} else {
targetSortName = hasFamilyNameHatnote ? `${nameParts[0]}, ${nameParts.slice(1).join(' ')}` : getSmartSortKey(tClean, entity);
const $dsMissing = $('<div>').addClass('comrade-container comrade-nudge');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text(`Add: {{DEFAULTSORT:${targetSortName}}}`).click(() => performSortCorrection(targetSortName, "add"));
$header.append($('<div>').append($('<strong>').text('DEFAULTSORT:'), ` Tag is missing. `, $btn));
$dsMissing.append($header);
appendDismiss($dsMissing);
}
const longName = getLongNameFromWikitext(wikitext);
if (longName && longName.length > tClean.length) {
let sortKey = hasFamilyNameHatnote ? `${longName.split(' ')[0]}, ${longName.split(' ').slice(1).join(' ')}` : getSmartSortKey(longName, entity);
const rLongWikitext = `#REDIRECT [[${currentTitle}]]\n\n{{Redirect category shell|\n{{R from long name}}\n}}\n{{DEFAULTSORT:${sortKey}}}`;
checkAndRenderRedirect(longName, currentTitle, null, "Long name", rLongWikitext);
}
if (targetSortName) {
let targetPage = currentTitle.includes(' (') ? currentTitle.split(' (')[0] : currentTitle;
let rCat = targetPage !== currentTitle ? "R from ambiguous sort name" : "R from sort name";
if (targetSortName.includes(',')) {
const p = targetSortName.split(',');
rCat += `|${p[0].trim().charAt(0).toUpperCase()}|${p[1].trim().charAt(0).toUpperCase()}`;
}
checkAndRenderRedirect(targetSortName, targetPage, rCat, "Sort name");
}
const finalFamilyNames = new Set();
const finalGivenNames = new Set();
surnames.forEach(fn => {
if (tClean.toLowerCase().includes(fn.toLowerCase())) {
fn.split(' ').forEach(part => finalFamilyNames.add(part));
finalFamilyNames.add(fn);
}
});
givens.forEach(gn => {
if (tClean.toLowerCase().includes(gn.toLowerCase())) finalGivenNames.add(gn);
});
if (finalFamilyNames.size === 0 && nameParts.length >= 2) finalFamilyNames.add(nameParts[nameParts.length - 1]);
if (finalGivenNames.size === 0 && nameParts.length >= 2) finalGivenNames.add(nameParts[0]);
const getPriorityTarget = async (name, type) => {
const titles = type === 'surname' ? [`List of people with surname ${name}`, `List of people with the English surname ${name}`, `${name} (surname)`] : [`List of people with given name ${name}`, `${name} (given name)`];
const res = await api.get({
action: 'query',
titles: titles.join('|'),
formatversion: 2
});
return titles.find(t => res.query.pages.find(pg => pg.title === t && !pg.missing));
};
for (const name of finalGivenNames) {
if (name.toLowerCase() === primarySurname.toLowerCase()) continue;
const target = await getPriorityTarget(name, 'given');
if (target) await checkNameList(name, 'given name', currentTitle, hasShortDesc, isOrphan, target);
}
for (const name of finalFamilyNames) {
const target = await getPriorityTarget(name, 'surname');
if (target) await checkNameList(name, 'surname', currentTitle, hasShortDesc, isOrphan, target);
}
}
}
} catch (err) {
console.error("Comrade error:", err);
}
});
}
})();
(function() {
if (window.IllWill) return;
const STRIKEOUT = true;
const HIDE = 'hide';
const IllWill = {
Version: "1.1.1",
Attribution: 'created by <a target="_blank" href="/wiki/User:Cobaltcigs">cobaltcigs</a>',
Summary: "Added interlanguage links using IllWill.js",
DataApi: "https://www.wikidata.org/w/api.php",
LocalLang: mw.config.get("wgPageContentLanguage"),
Disambiguator: / \(.*\)$/,
SiteLinks: {},
Config: {
MaxLanguages: 5,
IgnoreScientific: STRIKEOUT,
AutoDiff: true
},
css: `
#illwill { clear: both; margin-bottom: 1.5em; border: 1px solid #a2a9b1; padding: 15px; background: #f8f9fa; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.ill-sitelinks a { display: inline-block; margin-bottom: 3px; font-size: 0.9em; color: #36c; }
.ill-size-tag { font-size: 0.8em; color: #72777d; margin-left: 6px; white-space: nowrap; }
.illwill-ignore { text-decoration: line-through; color: #72777d; font-size: 0.85em; }
.ill-x { width: 35px; text-align: center; }
.ill-input, .ill-output { font-family: 'Courier New', monospace; white-space: pre-wrap; font-size: 0.9em; background: #fff; border: 1px solid #eaecf0; padding: 4px; border-radius: 2px; }
#ill-buttons { text-align: right; padding: 10px; border-top: 1px solid #a2a9b1; margin-top: 10px; }
#ill-buttons button { margin-left: 10px; cursor: pointer; padding: 6px 16px; border-radius: 2px; border: 1px solid #a2a9b1; transition: all 0.2s; }
#ill-ok { background: #36c; color: #fff; border-color: #36c !important; font-weight: bold; }
#ill-ok:hover { background: #447ff5; }
#ill-ok:disabled { background: #eaecf0; color: #72777d; border-color: #c8ccd1 !important; cursor: not-allowed; opacity: 0.7; }
#ill-table { width: 100%; border-collapse: collapse; margin-top: 10px; background: white; }
#ill-table th { background: #f2f2f2; text-align: left; padding: 8px; border: 1px solid #a2a9b1; }
#ill-table td { padding: 8px; border: 1px solid #a2a9b1; vertical-align: top; }
#ill-table caption { background: #eaffea; font-weight: bold; padding: 10px; border: 1px solid #a2a9b1; border-bottom: none; font-size: 1.1em; }
#ill-help { font-size: 0.85em; color: #54595d; padding: 10px; line-height: 1.4; }
.ill-loading-inline { font-style: italic; color: #72777d; font-size: 0.9em; display: block; margin: 5px 0; }
`,
setup() {
if (!["edit", "submit"].includes(mw.config.get('wgAction'))) return;
mw.loader.addStyleTag(this.css);
const link = mw.util.addPortletLink('p-cactions', '#', 'IllWill', 'ca-illwill', "Add interlanguage links via Wikidata");
if (link) {
link.addEventListener('click', (e) => {
e.preventDefault();
this.makePanel();
});
}
},
norm(s) {
if (!s) return "";
const t = s.trim().replace(/[\s_]+/g, " ");
return t.charAt(0).toUpperCase() + t.slice(1);
},
async getRedLinks($textbox) {
const txt = $textbox.textSelection('getContents') || "";
const wl = txt.match(/\[\[[^[\]\n]+\]\]/g);
if (!wl) return {
reds: [],
txt
};
const api = new mw.Api();
const textToParse = wl.join("").replace(/\|[^\]]+/g, "");
try {
const data = await api.post({
action: 'parse',
text: textToParse,
contentmodel: 'wikitext',
formatversion: 2
});
const tempDiv = document.createElement('div');
tempDiv.innerHTML = data.parse.text;
const reds = Array.from(tempDiv.querySelectorAll("a.new")).map(x => {
const urlParams = new URLSearchParams(x.href.split('?')[1]);
return urlParams.get('title')?.replace(/_/g, " ");
}).filter(Boolean);
return {
reds: [...new Set(reds)],
txt
};
} catch (e) {
return {
reds: [],
txt
};
}
},
async makePanel() {
const $editform = $('#editform');
const $textbox = $('#wpTextbox1');
$('#illwill').remove();
$editform.hide();
const $wrapper = $('<div id="illwill">Loading redlinks...</div>').insertBefore($editform);
const {
reds,
txt
} = await this.getRedLinks($textbox);
const wtLinks = [];
reds.forEach(title => {
const escaped = title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s/g, "[\\s_]+");
const re = new RegExp("\\[\\[\\s*" + escaped + "\\s*(?:\\|[^\\[\\]]+)?\\]\\]", "ig");
const match = txt.match(re);
if (match && !wtLinks.some(z => z.title === title)) {
wtLinks.push({
link: match[0],
title
});
}
});
if (!wtLinks.length) {
const $back = $('<button>Quit</button>').on('click', () => {
$wrapper.remove();
$editform.show();
});
$wrapper.empty().append($('<strong>No valid redlinks found in text.</strong> '), $back);
return;
}
const $table = $(`
<table id="ill-table" class="wikitable">
<caption>IllWill.js (v${this.Version})</caption>
<thead>
<tr>
<th class="ill-x"><input type="checkbox" id="ill-select-all" disabled></th>
<th>Input link</th>
<th>Possible Wikidata matches</th>
<th>Sitelinks (top ${this.Config.MaxLanguages} by size)</th>
<th>Resulting wikitext</th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<td colspan="3" id="ill-help">Carefully examine possible Wikidata matches.</td>
<td colspan="2" id="ill-buttons">
<button id="ill-cancel">Cancel</button>
<button id="ill-ok" disabled>Show changes</button>
</td>
</tr>
</tfoot>
</table>
`);
const $tbody = $table.find('tbody');
wtLinks.forEach((item, i) => {
$tbody.append(`
<tr id="ill-row-${i}" data-index="${i}">
<td class="ill-x"><input type="checkbox" class="ill-row-check" disabled></td>
<td class="ill-input">${this.escapeHTML(item.link)}</td>
<td class="ill-wd-item"><span class="ill-loading-inline">Searching...</span></td>
<td class="ill-sitelinks"></td>
<td class="ill-output"></td>
</tr>
`);
});
$wrapper.empty().append($table);
const chunkSize = 5;
for (let i = 0; i < wtLinks.length; i += chunkSize) {
const chunk = wtLinks.slice(i, i + chunkSize);
await Promise.all(chunk.map((item, idx) => this.fetchWikidataOptimized(item.title, i + idx)));
}
$table.on('click', '#ill-cancel', () => {
$editform.show();
$wrapper.remove();
});
$table.on('click', '#ill-ok', () => this.onOkButton($editform, $wrapper, $textbox));
$table.on('change', '#ill-select-all', (e) => {
const isChecked = $(e.target).is(':checked');
$table.find('.ill-row-check:not(:disabled)').each(function() {
$(this).prop('checked', isChecked).trigger('change');
});
});
$table.on('change', '.ill-row-check', (e) => {
this.onCheckbox(e);
this.updateOkButtonState($table);
});
$table.on('change', 'input[type="radio"]', (e) => {
this.showSiteLinks(e);
});
},
async fetchWikidataOptimized(title, index) {
const query = title.replace(this.Disambiguator, "");
const url = `${this.DataApi}?action=wbsearchentities&search=${encodeURIComponent(query)}&language=${this.LocalLang}&limit=5&format=json&origin=*`;
try {
const resp = await fetch(url);
const data = await resp.json();
const results = data.search;
const $cell = $(`#ill-row-${index} .ill-wd-item`);
if (!results || !results.length) {
$cell.html('<span class="ill-na">(no results)</span>');
return;
}
const entityIds = results.map(r => r.id);
await this.fetchEntities(entityIds, index, $cell);
} catch (e) {
$(`#ill-row-${index} .ill-wd-item`).text("Search failed.");
}
},
async fetchEntities(ids, rowIndex, $cell) {
const url = `${this.DataApi}?action=wbgetentities&ids=${ids.join('|')}&props=labels|descriptions|claims|sitelinks&format=json&origin=*`;
try {
const resp = await fetch(url);
const data = await resp.json();
$cell.empty();
ids.forEach(qNum => {
const entity = data.entities[qNum];
if (!entity) return;
const isScientific = entity.claims?.P31?.some(s => s.mainsnak?.datavalue?.value?.id === "Q13442814");
if (isScientific && this.Config.IgnoreScientific === HIDE) return;
const desc = entity.descriptions?.[this.LocalLang]?.value || entity.descriptions?.en?.value || "";
const label = entity.labels?.[this.LocalLang]?.value || entity.labels?.en?.value || qNum;
const isStrikeout = isScientific && this.Config.IgnoreScientific === STRIKEOUT;
const $div = $(`
<div class="${isStrikeout ? 'illwill-ignore' : ''}">
<input type="radio" name="selection-${rowIndex}" value="${qNum}" id="radio-${qNum}" ${isStrikeout ? 'disabled' : ''}>
<label for="radio-${qNum}">
<a href="https://wikidata.org/wiki/${qNum}" target="_blank">${this.escapeHTML(label)}</a>:
<span class="ill-desc">${desc ? this.escapeHTML(desc) : '<i>no description</i>'}</span>
</label>
</div>
`);
$cell.append($div);
this.SiteLinks[qNum] = entity.sitelinks || {};
});
} catch (e) {
$cell.text("Detail fetch failed.");
}
},
updateOkButtonState($table) {
const $rows = $table.find('.ill-row-check:not(:disabled)');
const $checkedRows = $rows.filter(':checked');
const anyEnabled = $rows.length > 0;
$table.find('#ill-ok').prop('disabled', $checkedRows.length === 0);
const $master = $table.find('#ill-select-all');
$master.prop('disabled', !anyEnabled);
if (anyEnabled && $rows.length === $checkedRows.length) {
$master.prop('checked', true);
} else {
$master.prop('checked', false);
}
},
async showSiteLinks(e) {
const isManualToggling = e.fromCheckbox || false;
const qNum = e.target.value;
const $row = $(e.target).closest('tr');
const sitelinks = this.SiteLinks[qNum];
const $checkbox = $row.find('.ill-row-check');
const $sitelinkCell = $row.find('.ill-sitelinks');
const $outputCell = $row.find('.ill-output');
const $table = $('#ill-table');
const effectiveMax = Math.min(Math.max(this.Config.MaxLanguages, 3), 6);
const validKeys = Object.keys(sitelinks).filter(k => k.endsWith('wiki') && !k.includes('commons'));
$sitelinkCell.html('<span class="ill-loading-inline">Measuring articles...</span>');
$outputCell.empty();
if (!isManualToggling) {
$checkbox.prop('disabled', true).prop('checked', false);
}
const candidates = validKeys.slice(0, 25);
const sizeMap = [];
await Promise.all(candidates.map(async (key) => {
const lang = key.replace('wiki', '').replace(/_/g, '-');
const title = sitelinks[key].title;
const endpoint = `https://${lang}.wikipedia.org/w/api.php?action=query&titles=${encodeURIComponent(title)}&prop=info&format=json&origin=*`;
try {
const res = await fetch(endpoint).then(r => r.json());
const page = Object.values(res.query.pages)[0];
sizeMap.push({
lang,
title,
length: page.length || 0
});
} catch (err) {
sizeMap.push({
lang,
title,
length: 0
});
}
}));
sizeMap.sort((a, b) => b.length - a.length);
const topLinks = sizeMap.slice(0, effectiveMax);
$sitelinkCell.html(topLinks.map(item => {
const kbValue = Math.round(item.length / 1024);
return `<div><a href="https://${item.lang}.wikipedia.org/wiki/${encodeURIComponent(item.title)}" target="_blank">${item.lang}:${this.escapeHTML(item.title)}</a> <span class="ill-size-tag">(${kbValue} kB)</span></div>`;
}).join(""));
const inputWikitext = $row.find('.ill-input').text();
const match = inputWikitext.match(/\[\[(.*?)\]\]/);
if (match) {
const parts = match[1].split('|');
const targetPage = parts[0].trim();
const surfaceName = (parts[1] || parts[0]).trim();
const illLangs = topLinks.map(item => {
const isSameTitle = this.norm(item.title) === this.norm(targetPage);
return `|${item.lang}|${isSameTitle ? '' : item.title}`;
}).join("");
const ltParam = (this.norm(surfaceName) === this.norm(targetPage)) ? "" : `|lt=${surfaceName}`;
$outputCell.text(`{{ill|${targetPage}${ltParam}${illLangs}}}`);
}
$checkbox.prop('disabled', !topLinks.length);
if (!isManualToggling) {
$checkbox.prop('checked', !!topLinks.length);
}
this.updateOkButtonState($table);
},
onCheckbox(e) {
const $row = $(e.target).closest('tr');
const $radio = $row.find('input[type="radio"]:checked');
const isChecked = $(e.target).is(':checked');
if (isChecked) {
if ($radio.length) {
this.showSiteLinks({
target: $radio[0],
fromCheckbox: true
});
}
} else {
$row.find('.ill-sitelinks, .ill-output').empty();
}
},
onOkButton($editform, $wrapper, $textbox) {
let text = $textbox.textSelection('getContents');
let changed = false;
$wrapper.find('.ill-row-check:checked').each(function() {
const $row = $(this).closest('tr');
const input = $row.find('.ill-input').text();
const output = $row.find('.ill-output').text();
if (output) {
const escapedInput = input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
text = text.replace(new RegExp(escapedInput, "gi"), output);
changed = true;
}
});
if (changed) {
$('#wpSummary').val((i, v) => (v ? v + "; " : "") + this.Summary);
$textbox.textSelection('setContents', text);
$wrapper.remove();
$editform.show();
if (this.Config.AutoDiff) $('#wpDiff').click();
}
},
escapeHTML(str) {
return str.replace(/[&<>"']/g, m => ({
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
})[m]);
}
};
window.IllWill = IllWill;
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'jquery.textSelection'], () => {
$(() => IllWill.setup());
});
})();
//</nowiki>
cl9kmdwzqw1qplqt1sk20x76v6oz5xo
739072
739071
2026-04-22T08:31:45Z
Sam Sailor
26820
Test
739072
javascript
text/javascript
//<nowiki>
(function() {
var Comrade = Comrade || {};
mw.util.addCSS(`
.ambox-Orphan { display: block !important; }
.comrade-container {
box-shadow: 0 1px 3px rgba(0,0,0,0.1); border-radius: 2px;
padding: 10px 15px; margin: 10px 0; display: flex;
flex-direction: column; align-items: flex-start; gap: 8px;
border: 1px solid #a2a9b1; background: #fcfcfc; line-height: 1.5;
}
.comrade-header { display: flex; align-items: center; justify-content: space-between; width: 100%; }
.comrade-content { display: flex; flex-direction: column; gap: 5px; width: 100%; }
.comrade-success { background-color: #d5f5e3 !important; border-left: 5px solid #27ae60 !important; color: #1b5e20 !important; }
.comrade-nudge { background: #fff9ea; border-left: 5px solid #f0ad4e; }
.comrade-info { background: #eaf3ff; border-left: 5px solid #36c; }
.comrade-status { background: #eaf3ff; border-left: 5px solid #36c; }
.comrade-container button:not(.comrade-dismiss) {
background: #f8f9fa; border: 1px solid #a2a9b1; border-radius: 2px;
padding: 2px 10px; cursor: pointer; font-weight: bold; font-size: 0.95em; margin-left: 10px;
}
.comrade-container button:not(.comrade-dismiss):hover { border-color: #36c; color: #36c; background: #fff; }
.comrade-dismiss {
background: transparent; border: none; color: #d33; font-weight: bold;
cursor: pointer; font-size: 0.9em;
}
.comrade-dismiss:hover { text-decoration: underline; }
.comrade-code-block { background: #f1f1f1; padding: 4px 8px; border-radius: 3px; font-family: monospace; display: inline-block; margin-top: 4px; }
`);
function appendDismiss($el) {
$('<button>').addClass('comrade-dismiss').text('Dismiss').click(function() {
$(this).closest('.comrade-container').fadeOut();
}).appendTo($el.find('.comrade-header'));
$("#siteSub").after($el);
}
async function checkAndRenderRedirect(redirTitle, targetTitle, rCategory, labelText, customWikitext) {
const api = new mw.Api();
const res = await api.get({
action: 'query',
titles: redirTitle,
formatversion: 2
});
if (res.query.pages[0].missing) {
const safeId = "comrade-redir-" + btoa(unescape(encodeURIComponent(redirTitle))).replace(/=/g, "");
const $container = $('<div>').addClass('comrade-container comrade-info').attr('id', safeId);
const $header = $('<div>').addClass('comrade-header');
const summary = `Created redirect from ${labelText.toLowerCase()} to [[${targetTitle}]]`;
const $btn = $('<button>').text('Create redirect').click(() => createModernRedirect(redirTitle, targetTitle, rCategory, summary, safeId, customWikitext));
$header.append($('<div>').append($('<strong>').text(`${labelText}: `), `Create redirect from `, $('<code>').text(redirTitle), $btn));
$container.append($header);
appendDismiss($container);
}
}
async function checkDomainRedirect(qid, currentTitle) {
let domainCandidate = "";
if (qid) {
try {
const res = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetclaims',
entity: qid,
property: 'P856',
format: 'json',
origin: '*'
}
});
if (res.claims?.P856) {
domainCandidate = res.claims.P856[0].mainsnak.datavalue.value;
}
} catch (e) {
console.log("Comrade: Wikidata API call failed.");
}
}
if (!domainCandidate) {
domainCandidate = $(".infobox .url a").last().attr("href") || $(".official-website a").first().attr("href");
}
if (domainCandidate) {
try {
const url = new URL(domainCandidate);
let domain = url.hostname.replace(/^www\d*\./, "");
if (domain.includes(".") && url.pathname === "/") {
const citationURL = '/api/rest_v1/data/citation/mediawiki/' + encodeURIComponent(domainCandidate);
$.ajax({
url: citationURL,
method: 'GET',
success: function() {
checkAndRenderRedirect(domain, currentTitle, "R from domain name", "Domain");
}
});
}
} catch (e) {
console.log("Comrade: Error parsing URL object.");
}
}
}
async function checkNameList(name, type, currentTitle, hasShortDesc, isOrphan) {
const api = new mw.Api();
const candidates = [`${name} (${type})`, `${name} (${type.toLowerCase()})`, `${name} (name)`, name];
const checked = new Set();
for (const title of candidates) {
if (checked.has(title)) continue;
checked.add(title);
const res = await api.get({
action: 'query',
prop: 'revisions',
titles: title,
rvprop: 'content',
formatversion: 2,
redirects: 0
});
const page = res.query.pages[0];
if (page && !page.missing) {
const text = page.revisions[0].content;
const resolvedTitle = page.title;
const isNameMatch = /\{\{(Surname|Hndis|Given[ _]name|Forename|Givenname)/i.test(text);
const isNameDab = /\{\{(Disambiguation|Dab|Disambig)(?:[^}]*\|(surname|surnames|given[ _]name|given[ _]names)|(?:\s*\}\}))/i.test(text);
if (isNameMatch || isNameDab) {
const escapedTitle = currentTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/ /g, '[ _]');
const alreadyListed = new RegExp('(\\[\\[|\\{\\{anbl\\|[ _]*|\\|)' + escapedTitle, 'i').test(text);
if (alreadyListed) break;
const $container = $('<div>').addClass('comrade-container');
const $header = $('<div>').addClass('comrade-header');
const $content = $('<div>').addClass('comrade-content');
if (isOrphan) {
$container.addClass('comrade-nudge');
$header.append($('<strong>').text('Comrade nudge:'));
} else {
$container.addClass('comrade-info');
$header.append($('<strong>').text('Informative:'));
}
$content.append($('<span>').append(`${type.charAt(0).toUpperCase() + type.slice(1)} not listed at `, $('<a>').attr({
href: mw.util.getUrl(resolvedTitle),
target: '_blank'
}).text(resolvedTitle), "; should it be?"));
const snippet = '* {{anbl|' + currentTitle + '}}';
const $snippetWrapper = $('<div>').css({
'display': 'flex',
'align-items': 'center',
'gap': '10px',
'margin-top': '4px'
});
const $instructionSpan = $('<span>').append('If the Short description is good, consider adding ', $('<code>').text(snippet).css({
'background-color': '#f8f9fa',
'border': '1px solid #eaecf0',
'border-radius': '2px',
'padding': '1px 4px'
}));
const $copyBtn = $('<button>').text('Copy').css('margin-left', '0').click(function() {
navigator.clipboard.writeText(snippet).then(() => {
const $this = $(this);
const originalText = $this.text();
$this.text('Copied!').prop('disabled', true);
setTimeout(() => {
$this.text(originalText).prop('disabled', false);
}, 1500);
});
});
$snippetWrapper.append($instructionSpan, $copyBtn);
$content.append($snippetWrapper);
if (!hasShortDesc) {
$content.append($('<span>').css({
'color': '#d33',
'font-weight': 'bold',
'margin-top': '5px'
}).text('⚠️ Missing short description: Please add a concise one before listing.'));
}
$container.append($header, $content);
appendDismiss($container);
break;
}
}
}
}
async function createModernRedirect(redirTitle, targetTitle, rCategory, customSummary, elementId, customWikitext) {
const api = new mw.Api();
const content = customWikitext || `#REDIRECT [[${targetTitle}]]\n\n{{Redirect category shell|\n{{${rCategory}}}\n}}`;
const summary = customSummary || `Created redirect from [[${redirTitle}]]`;
return api.postWithToken('csrf', {
action: 'edit',
title: redirTitle,
text: content,
summary: summary,
createonly: true
}).done(() => {
mw.notify("Redirect created!", {
type: 'success',
tag: 'comrade',
classes: ['comrade-success']
});
$(`#${elementId}`).fadeOut();
});
}
function getSmartSortKey(fullName, entity) {
const countryIds = entity.claims?.P27?.map(c => c.mainsnak?.datavalue?.value?.id) || [];
const locationIds = entity.claims?.P17?.map(c => c.mainsnak?.datavalue?.value?.id) || [];
const placeIds = [...(entity.claims?.P19 || []), ...(entity.claims?.P119 || [])].map(c => c.mainsnak?.datavalue?.value?.id).filter(Boolean);
const allRelevantIds = [...countryIds, ...locationIds, ...placeIds];
const birthYear = parseInt(entity.claims?.P569?.[0]?.mainsnak?.datavalue?.value?.time?.match(/[+-](\d{4})/)?.[1]) || 2026;
if (fullName.startsWith("Saint ")) return fullName.replace("Saint ", "");
const arabicParticles = /\b(Abu|Abd|Abdel|Abdul|ben|bin|bint)\b/i;
if (fullName.match(arabicParticles)) {
if (birthYear > 1900) {
const parts = fullName.split(' ');
const binIndex = parts.findIndex(p => p.toLowerCase() === 'bin' || p.toLowerCase() === 'ben');
if (binIndex > 0) return parts.slice(binIndex).join(' ') + ', ' + parts.slice(0, binIndex).join(' ');
} else {
return fullName;
}
}
const eastAsianQids = ["Q148", "Q184", "Q884", "Q424", "Q881", "Q17"];
if (allRelevantIds.some(id => eastAsianQids.includes(id))) {
if (allRelevantIds.includes("Q17") && birthYear > 1885) return westernSort(fullName);
const parts = fullName.split(' ');
if (parts.length > 1) return `${parts[0]}, ${parts.slice(1).join(' ')}`;
}
return westernSort(fullName);
}
function westernSort(name) {
let cleanName = name;
if (name.startsWith("O'")) cleanName = name.replace("O'", "O");
const parts = cleanName.split(' ');
if (parts.length < 2) return cleanName;
const surname = parts.pop();
if (["Jr.", "Jr", "III", "II", "Sr.", "Sr"].includes(surname)) {
const actualSurname = parts.pop();
return `${actualSurname}, ${parts.join(' ')} ${surname}`;
}
return `${surname}, ${parts.join(' ')}`;
}
async function performDeorphan() {
const api = new mw.Api();
await api.edit(mw.config.get('wgPageName'), function(rev) {
let text = rev.content;
const summary = 'Article has backlinks; removed [[Template:Orphan|{{Orphan}}]]';
text = text.replace(/\{\{Orphan\s*(?:\|[^}]*)?\}\}\s*/gi, '');
text = text.replace(/(\{\{Multiple[ _]issues\s*)\|\s*\|/gi, '$1|');
text = replaceMI(text);
text = text.replace(/\n{3,}/g, '\n\n').trim();
return {
text,
summary,
minor: true
};
});
mw.notify('Orphan tag removed!', {
type: 'success',
tag: 'comrade',
classes: ['comrade-success']
});
setTimeout(() => location.reload(), 700);
}
function replaceMI(text) {
const miStart = /\{\{Multiple[ _]issues\s*\|/gi;
let result = '';
let lastIndex = 0;
let match;
while ((match = miStart.exec(text)) !== null) {
const start = match.index;
result += text.slice(lastIndex, start);
let depth = 0;
let i = start;
while (i < text.length) {
if (text[i] === '{' && text[i + 1] === '{') {
depth++;
i += 2;
} else if (text[i] === '}' && text[i + 1] === '}') {
depth--;
i += 2;
if (depth === 0) break;
} else {
i++;
}
}
const end = i;
const full = text.slice(start, end);
const pipeIdx = full.indexOf('|');
const inner = full.slice(pipeIdx + 1, full.length - 2).trim();
const topLevel = extractTopLevelTemplates(inner);
if (topLevel.length === 0) {
result += '';
} else if (topLevel.length === 1) {
result += topLevel[0].trim();
} else {
result += full;
}
lastIndex = end;
miStart.lastIndex = lastIndex;
}
result += text.slice(lastIndex);
return result;
}
function extractTopLevelTemplates(inner) {
const templates = [];
let i = 0;
while (i < inner.length) {
if (inner[i] === '{' && inner[i + 1] === '{') {
let depth = 0;
const start = i;
while (i < inner.length) {
if (inner[i] === '{' && inner[i + 1] === '{') {
depth++;
i += 2;
} else if (inner[i] === '}' && inner[i + 1] === '}') {
depth--;
i += 2;
if (depth === 0) break;
} else {
i++;
}
}
templates.push(inner.slice(start, i));
} else {
i++;
}
}
return templates;
}
async function performSortCorrection(newName, type) {
const api = new mw.Api();
const title = mw.config.get("wgPageName");
try {
const data = await api.get({
action: 'query',
prop: 'revisions',
titles: title,
rvprop: 'content',
formatversion: 2
});
let content = data.query.pages[0].revisions[0].content;
const dsRegex = /\{\{(?:DEFAULTSORT|Defaultsort):[^}]+\}\}/i;
const newTag = `{{DEFAULTSORT:${newName}}}`;
let actionDesc = type === "add" ? "added" : "corrected";
if (dsRegex.test(content)) {
content = content.replace(dsRegex, newTag);
} else {
const catRegex = /\[\[Category:/i;
if (catRegex.test(content)) {
content = content.replace(catRegex, `${newTag}\n[[Category:`);
} else {
content += `\n\n${newTag}`;
}
}
await api.postWithToken('csrf', {
action: 'edit',
title: title,
text: content,
summary: `Setting DEFAULTSORT to ${newName}`,
nocreate: true
});
mw.notify(`DEFAULTSORT ${actionDesc} to ${newName}!`, {
title: 'Comrade Success',
type: 'success'
});
setTimeout(() => location.reload(), 700);
} catch (err) {
mw.notify('Failed to save DEFAULTSORT.', {
type: 'error'
});
}
}
function stripDiacritics(str) {
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}
function renderSortNudge(target, reason) {
const $dsNudge = $('<div>').addClass('comrade-container comrade-nudge');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text(`Correct to: ${target}`).click(() => performSortCorrection(target, "format"));
$header.append($('<div>').append($('<strong>').text('DEFAULTSORT:'), ` ${reason} `, $btn));
$dsNudge.append($header);
appendDismiss($dsNudge);
}
async function fetchWikidataNameLabels(entity) {
const familyIds = entity.claims?.P734?.map(c => c.mainsnak.datavalue.value.id) || [];
const givenIds = entity.claims?.P735?.map(c => c.mainsnak.datavalue.value.id) || [];
const allNameIds = [...new Set([...familyIds, ...givenIds])];
if (allNameIds.length === 0) return {
givens: [],
surnames: []
};
const nameData = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetentities',
ids: allNameIds.join('|'),
props: 'labels',
languages: 'en',
format: 'json',
origin: '*'
},
dataType: 'json'
});
return {
givens: givenIds.map(id => nameData.entities[id]?.labels?.en?.value).filter(Boolean),
surnames: familyIds.map(id => nameData.entities[id]?.labels?.en?.value).filter(Boolean)
};
}
function fixNameCasing(part, referenceName) {
return part.split(' ').map(word => {
const match = referenceName.match(new RegExp(`\\b${word}\\b`, 'i'));
if (match) return match[0];
return referenceName.split(' ').find(tWord => word.toLowerCase().startsWith(tWord.toLowerCase()) || tWord.toLowerCase().startsWith(word.toLowerCase())) || word;
}).join(' ');
}
function getLongNameFromWikitext(wikitext) {
const firstLine = wikitext.split('\n').find(l => l.includes("'''"));
if (!firstLine) return null;
const boldMatch = firstLine.match(/'''(.+?)'''/);
if (!boldMatch) return null;
return boldMatch[1].replace(/\s*([“"«《„‹「『'].+?[”"»》”›」』']|\([^)\n]*\))\s*/g, ' ').replace(/\s+/g, ' ').trim();
}
if (mw.config.get("wgDBname") === "enwiki" && mw.config.get("wgUserId") === 19244234 && mw.config.get("wgNamespaceNumber") === 0 && mw.config.get("wgAction") === "view") {
$(document).ready(async function() {
const api = new mw.Api();
const qid = mw.config.get("wgWikibaseItemId");
try {
const [pageData, backlinkData] = await Promise.all([
api.get({
action: 'query',
prop: 'revisions',
titles: mw.config.get("wgPageName"),
rvprop: 'content',
formatversion: 2
}),
api.get({
action: 'query',
list: 'backlinks',
blfilterredir: 'nonredirects',
bllimit: 50,
blnamespace: 0,
bltitle: mw.config.get("wgPageName"),
formatversion: 2
})
]);
const page = pageData.query.pages[0];
if (!page || page.missing || !page.revisions) return;
const wikitext = page.revisions[0].content;
const currentTitle = mw.config.get("wgTitle");
const cleanTitle = stripDiacritics(currentTitle);
const isOrphanTagged = /\{\{\s*(Orphan|Do-attempt|Lonely|Orp|Orphaned article)/i.test(wikitext) || /\| *orphan *=/i.test(wikitext);
const backLinkCount = backlinkData.query.backlinks.length;
if (isOrphanTagged && backLinkCount >= 1) {
const $container = $('<div>').addClass('comrade-container comrade-status');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text('Remove orphan tag').click(() => performDeorphan());
$header.append($('<div>').append($('<strong>').text('Status:'), ` Article has ${backLinkCount} backlink(s).`, $btn));
$container.append($header);
appendDismiss($container);
}
if (cleanTitle !== currentTitle) checkAndRenderRedirect(cleanTitle, currentTitle, "R to diacritic", "Diacritic");
checkDomainRedirect(qid, currentTitle);
if (qid) {
const wikiData = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetentities',
ids: qid,
props: 'claims|labels',
languages: 'en',
format: 'json',
origin: '*'
},
dataType: 'json'
});
const entity = wikiData.entities[qid];
const isHuman = entity?.claims?.P31?.some(c => c.mainsnak?.datavalue?.value?.id === "Q5");
const tClean = currentTitle.replace(/\s*\(.*?\)\s*/g, '').trim();
const nameParts = tClean.split(' ');
const hasFamilyNameHatnote = /\{\{family[ _]name[ _]hatnote/i.test(wikitext);
const hasShortDesc = /\{\{\s*[Ss]hort description/i.test(wikitext);
const dsMatch = wikitext.match(/\{\{(?:DEFAULTSORT|Defaultsort):([^}]+)\}\}/i);
const isOrphan = isOrphanTagged && backLinkCount < 1;
const primarySurname = hasFamilyNameHatnote ? nameParts[0] : (getSmartSortKey(tClean, entity).split(',')[0] || nameParts[nameParts.length - 1]);
let targetSortName = "";
if (isHuman) {
const {
givens,
surnames
} = await fetchWikidataNameLabels(entity);
const refName = entity.labels?.en?.value || currentTitle;
if (dsMatch) {
const currentDS = dsMatch[1].trim();
targetSortName = currentDS;
if (currentDS.includes(',')) {
let [lastPart, firstPart] = currentDS.split(',').map(s => s.trim());
if (firstPart.toLowerCase().endsWith(lastPart.toLowerCase())) {
firstPart = firstPart.substring(0, firstPart.length - lastPart.length).trim();
targetSortName = `${fixNameCasing(lastPart, refName)}, ${fixNameCasing(firstPart, refName)}`;
renderSortNudge(targetSortName, "Redundant surname in given name field.");
} else if (givens.length > 0 && lastPart.split(' ').length > 1) {
const misplacedGiven = lastPart.split(' ').find(part => givens.some(gn => gn.toLowerCase() === part.toLowerCase()));
if (misplacedGiven) {
const newSurname = lastPart.split(' ').filter(p => p !== misplacedGiven).join(' ');
targetSortName = `${fixNameCasing(newSurname, refName)}, ${fixNameCasing(misplacedGiven + ' ' + firstPart, refName)}`;
renderSortNudge(targetSortName, `Wikidata suggests "${misplacedGiven}" is a given name.`);
}
}
}
} else {
targetSortName = hasFamilyNameHatnote ? `${nameParts[0]}, ${nameParts.slice(1).join(' ')}` : getSmartSortKey(tClean, entity);
const $dsMissing = $('<div>').addClass('comrade-container comrade-nudge');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text(`Add: {{DEFAULTSORT:${targetSortName}}}`).click(() => performSortCorrection(targetSortName, "add"));
$header.append($('<div>').append($('<strong>').text('DEFAULTSORT:'), ` Tag is missing. `, $btn));
$dsMissing.append($header);
appendDismiss($dsMissing);
}
const longName = getLongNameFromWikitext(wikitext);
if (longName && longName.length > tClean.length) {
let sortKey = hasFamilyNameHatnote ? `${longName.split(' ')[0]}, ${longName.split(' ').slice(1).join(' ')}` : getSmartSortKey(longName, entity);
const rLongWikitext = `#REDIRECT [[${currentTitle}]]\n\n{{Redirect category shell|\n{{R from long name}}\n}}\n{{DEFAULTSORT:${sortKey}}}`;
checkAndRenderRedirect(longName, currentTitle, null, "Long name", rLongWikitext);
}
if (targetSortName) {
let targetPage = currentTitle.includes(' (') ? currentTitle.split(' (')[0] : currentTitle;
let rCat = targetPage !== currentTitle ? "R from ambiguous sort name" : "R from sort name";
if (targetSortName.includes(',')) {
const p = targetSortName.split(',');
rCat += `|${p[0].trim().charAt(0).toUpperCase()}|${p[1].trim().charAt(0).toUpperCase()}`;
}
checkAndRenderRedirect(targetSortName, targetPage, rCat, "Sort name");
}
const finalFamilyNames = new Set();
const finalGivenNames = new Set();
surnames.forEach(fn => {
if (tClean.toLowerCase().includes(fn.toLowerCase())) {
fn.split(' ').forEach(part => finalFamilyNames.add(part));
finalFamilyNames.add(fn);
}
});
givens.forEach(gn => {
if (tClean.toLowerCase().includes(gn.toLowerCase())) finalGivenNames.add(gn);
});
if (finalFamilyNames.size === 0 && nameParts.length >= 2) finalFamilyNames.add(nameParts[nameParts.length - 1]);
if (finalGivenNames.size === 0 && nameParts.length >= 2) finalGivenNames.add(nameParts[0]);
const getPriorityTarget = async (name, type) => {
const titles = type === 'surname' ? [`List of people with surname ${name}`, `List of people with the English surname ${name}`, `${name} (surname)`] : [`List of people with given name ${name}`, `${name} (given name)`];
const res = await api.get({
action: 'query',
titles: titles.join('|'),
formatversion: 2
});
return titles.find(t => res.query.pages.find(pg => pg.title === t && !pg.missing));
};
for (const name of finalGivenNames) {
if (name.toLowerCase() === primarySurname.toLowerCase()) continue;
const target = await getPriorityTarget(name, 'given');
if (target) await checkNameList(name, 'given name', currentTitle, hasShortDesc, isOrphan, target);
}
for (const name of finalFamilyNames) {
const target = await getPriorityTarget(name, 'surname');
if (target) await checkNameList(name, 'surname', currentTitle, hasShortDesc, isOrphan, target);
}
}
}
} catch (err) {
console.error("Comrade error:", err);
}
});
}
})();
(function() {
if (window.IllWill) return;
const STRIKEOUT = true;
const HIDE = 'hide';
const IllWill = {
Version: "1.1.1",
Attribution: 'created by <a target="_blank" href="/wiki/User:Cobaltcigs">cobaltcigs</a>',
Summary: "Added interlanguage links using IllWill.js",
DataApi: "https://www.wikidata.org/w/api.php",
LocalLang: mw.config.get("wgPageContentLanguage"),
Disambiguator: / \(.*\)$/,
SiteLinks: {},
Config: {
MaxLanguages: 5,
IgnoreScientific: STRIKEOUT,
AutoDiff: true
},
css: `
#illwill { clear: both; margin-bottom: 1.5em; border: 1px solid #a2a9b1; padding: 15px; background: #f8f9fa; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.ill-sitelinks a { display: inline-block; margin-bottom: 3px; font-size: 0.9em; color: #36c; }
.ill-size-tag { font-size: 0.8em; color: #72777d; margin-left: 6px; white-space: nowrap; }
.illwill-ignore { text-decoration: line-through; color: #72777d; font-size: 0.85em; }
.ill-x { width: 35px; text-align: center; }
.ill-input, .ill-output { font-family: 'Courier New', monospace; white-space: pre-wrap; font-size: 0.9em; background: #fff; border: 1px solid #eaecf0; padding: 4px; border-radius: 2px; }
#ill-buttons { text-align: right; padding: 10px; border-top: 1px solid #a2a9b1; margin-top: 10px; }
#ill-buttons button { margin-left: 10px; cursor: pointer; padding: 6px 16px; border-radius: 2px; border: 1px solid #a2a9b1; transition: all 0.2s; }
#ill-ok { background: #36c; color: #fff; border-color: #36c !important; font-weight: bold; }
#ill-ok:hover { background: #447ff5; }
#ill-ok:disabled { background: #eaecf0; color: #72777d; border-color: #c8ccd1 !important; cursor: not-allowed; opacity: 0.7; }
#ill-table { width: 100%; border-collapse: collapse; margin-top: 10px; background: white; }
#ill-table th { background: #f2f2f2; text-align: left; padding: 8px; border: 1px solid #a2a9b1; }
#ill-table td { padding: 8px; border: 1px solid #a2a9b1; vertical-align: top; }
#ill-table caption { background: #eaffea; font-weight: bold; padding: 10px; border: 1px solid #a2a9b1; border-bottom: none; font-size: 1.1em; }
#ill-help { font-size: 0.85em; color: #54595d; padding: 10px; line-height: 1.4; }
.ill-loading-inline { font-style: italic; color: #72777d; font-size: 0.9em; display: block; margin: 5px 0; }
`,
setup() {
if (!["edit", "submit"].includes(mw.config.get('wgAction'))) return;
mw.loader.addStyleTag(this.css);
const link = mw.util.addPortletLink('p-cactions', '#', 'IllWill', 'ca-illwill', "Add interlanguage links via Wikidata");
if (link) {
link.addEventListener('click', (e) => {
e.preventDefault();
this.makePanel();
});
}
},
norm(s) {
if (!s) return "";
const t = s.trim().replace(/[\s_]+/g, " ");
return t.charAt(0).toUpperCase() + t.slice(1);
},
async getRedLinks($textbox) {
const txt = $textbox.textSelection('getContents') || "";
const wl = txt.match(/\[\[[^[\]\n]+\]\]/g);
if (!wl) return {
reds: [],
txt
};
const api = new mw.Api();
const textToParse = wl.join("").replace(/\|[^\]]+/g, "");
try {
const data = await api.post({
action: 'parse',
text: textToParse,
contentmodel: 'wikitext',
formatversion: 2
});
const tempDiv = document.createElement('div');
tempDiv.innerHTML = data.parse.text;
const reds = Array.from(tempDiv.querySelectorAll("a.new")).map(x => {
const urlParams = new URLSearchParams(x.href.split('?')[1]);
return urlParams.get('title')?.replace(/_/g, " ");
}).filter(Boolean);
return {
reds: [...new Set(reds)],
txt
};
} catch (e) {
return {
reds: [],
txt
};
}
},
async makePanel() {
const $editform = $('#editform');
const $textbox = $('#wpTextbox1');
$('#illwill').remove();
$editform.hide();
const $wrapper = $('<div id="illwill">Loading redlinks...</div>').insertBefore($editform);
const {
reds,
txt
} = await this.getRedLinks($textbox);
const wtLinks = [];
reds.forEach(title => {
const escaped = title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s/g, "[\\s_]+");
const re = new RegExp("\\[\\[\\s*" + escaped + "\\s*(?:\\|[^\\[\\]]+)?\\]\\]", "ig");
const match = txt.match(re);
if (match && !wtLinks.some(z => z.title === title)) {
wtLinks.push({
link: match[0],
title
});
}
});
if (!wtLinks.length) {
const $back = $('<button>Quit</button>').on('click', () => {
$wrapper.remove();
$editform.show();
});
$wrapper.empty().append($('<strong>No valid redlinks found in text.</strong> '), $back);
return;
}
const $table = $(`
<table id="ill-table" class="wikitable">
<caption>IllWill.js (v${this.Version})</caption>
<thead>
<tr>
<th class="ill-x"><input type="checkbox" id="ill-select-all" disabled></th>
<th>Input link</th>
<th>Possible Wikidata matches</th>
<th>Sitelinks (top ${this.Config.MaxLanguages} by size)</th>
<th>Resulting wikitext</th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<td colspan="3" id="ill-help">Carefully examine possible Wikidata matches.</td>
<td colspan="2" id="ill-buttons">
<button id="ill-cancel">Cancel</button>
<button id="ill-ok" disabled>Show changes</button>
</td>
</tr>
</tfoot>
</table>
`);
const $tbody = $table.find('tbody');
wtLinks.forEach((item, i) => {
$tbody.append(`
<tr id="ill-row-${i}" data-index="${i}">
<td class="ill-x"><input type="checkbox" class="ill-row-check" disabled></td>
<td class="ill-input">${this.escapeHTML(item.link)}</td>
<td class="ill-wd-item"><span class="ill-loading-inline">Searching...</span></td>
<td class="ill-sitelinks"></td>
<td class="ill-output"></td>
</tr>
`);
});
$wrapper.empty().append($table);
const chunkSize = 5;
for (let i = 0; i < wtLinks.length; i += chunkSize) {
const chunk = wtLinks.slice(i, i + chunkSize);
await Promise.all(chunk.map((item, idx) => this.fetchWikidataOptimized(item.title, i + idx)));
}
$table.on('click', '#ill-cancel', () => {
$editform.show();
$wrapper.remove();
});
$table.on('click', '#ill-ok', () => this.onOkButton($editform, $wrapper, $textbox));
$table.on('change', '#ill-select-all', (e) => {
const isChecked = $(e.target).is(':checked');
$table.find('.ill-row-check:not(:disabled)').each(function() {
$(this).prop('checked', isChecked).trigger('change');
});
});
$table.on('change', '.ill-row-check', (e) => {
this.onCheckbox(e);
this.updateOkButtonState($table);
});
$table.on('change', 'input[type="radio"]', (e) => {
this.showSiteLinks(e);
});
},
async fetchWikidataOptimized(title, index) {
const query = title.replace(this.Disambiguator, "");
const url = `${this.DataApi}?action=wbsearchentities&search=${encodeURIComponent(query)}&language=${this.LocalLang}&limit=5&format=json&origin=*`;
try {
const resp = await fetch(url);
const data = await resp.json();
const results = data.search;
const $cell = $(`#ill-row-${index} .ill-wd-item`);
if (!results || !results.length) {
$cell.html('<span class="ill-na">(no results)</span>');
return;
}
const entityIds = results.map(r => r.id);
await this.fetchEntities(entityIds, index, $cell);
} catch (e) {
$(`#ill-row-${index} .ill-wd-item`).text("Search failed.");
}
},
async fetchEntities(ids, rowIndex, $cell) {
const url = `${this.DataApi}?action=wbgetentities&ids=${ids.join('|')}&props=labels|descriptions|claims|sitelinks&format=json&origin=*`;
try {
const resp = await fetch(url);
const data = await resp.json();
$cell.empty();
ids.forEach(qNum => {
const entity = data.entities[qNum];
if (!entity) return;
const isScientific = entity.claims?.P31?.some(s => s.mainsnak?.datavalue?.value?.id === "Q13442814");
if (isScientific && this.Config.IgnoreScientific === HIDE) return;
const desc = entity.descriptions?.[this.LocalLang]?.value || entity.descriptions?.en?.value || "";
const label = entity.labels?.[this.LocalLang]?.value || entity.labels?.en?.value || qNum;
const isStrikeout = isScientific && this.Config.IgnoreScientific === STRIKEOUT;
const $div = $(`
<div class="${isStrikeout ? 'illwill-ignore' : ''}">
<input type="radio" name="selection-${rowIndex}" value="${qNum}" id="radio-${qNum}" ${isStrikeout ? 'disabled' : ''}>
<label for="radio-${qNum}">
<a href="https://wikidata.org/wiki/${qNum}" target="_blank">${this.escapeHTML(label)}</a>:
<span class="ill-desc">${desc ? this.escapeHTML(desc) : '<i>no description</i>'}</span>
</label>
</div>
`);
$cell.append($div);
this.SiteLinks[qNum] = entity.sitelinks || {};
});
} catch (e) {
$cell.text("Detail fetch failed.");
}
},
updateOkButtonState($table) {
const $rows = $table.find('.ill-row-check:not(:disabled)');
const $checkedRows = $rows.filter(':checked');
const anyEnabled = $rows.length > 0;
$table.find('#ill-ok').prop('disabled', $checkedRows.length === 0);
const $master = $table.find('#ill-select-all');
$master.prop('disabled', !anyEnabled);
if (anyEnabled && $rows.length === $checkedRows.length) {
$master.prop('checked', true);
} else {
$master.prop('checked', false);
}
},
async showSiteLinks(e) {
const isManualToggling = e.fromCheckbox || false;
const qNum = e.target.value;
const $row = $(e.target).closest('tr');
const sitelinks = this.SiteLinks[qNum];
const $checkbox = $row.find('.ill-row-check');
const $sitelinkCell = $row.find('.ill-sitelinks');
const $outputCell = $row.find('.ill-output');
const $table = $('#ill-table');
const effectiveMax = Math.min(Math.max(this.Config.MaxLanguages, 3), 6);
const validKeys = Object.keys(sitelinks).filter(k => k.endsWith('wiki') && !k.includes('commons'));
$sitelinkCell.html('<span class="ill-loading-inline">Measuring articles...</span>');
$outputCell.empty();
if (!isManualToggling) {
$checkbox.prop('disabled', true).prop('checked', false);
}
const candidates = validKeys.slice(0, 25);
const sizeMap = [];
await Promise.all(candidates.map(async (key) => {
const lang = key.replace('wiki', '').replace(/_/g, '-');
const title = sitelinks[key].title;
const endpoint = `https://${lang}.wikipedia.org/w/api.php?action=query&titles=${encodeURIComponent(title)}&prop=info&format=json&origin=*`;
try {
const res = await fetch(endpoint).then(r => r.json());
const page = Object.values(res.query.pages)[0];
sizeMap.push({
lang,
title,
length: page.length || 0
});
} catch (err) {
sizeMap.push({
lang,
title,
length: 0
});
}
}));
sizeMap.sort((a, b) => b.length - a.length);
const topLinks = sizeMap.slice(0, effectiveMax);
$sitelinkCell.html(topLinks.map(item => {
const kbValue = Math.round(item.length / 1024);
return `<div><a href="https://${item.lang}.wikipedia.org/wiki/${encodeURIComponent(item.title)}" target="_blank">${item.lang}:${this.escapeHTML(item.title)}</a> <span class="ill-size-tag">(${kbValue} kB)</span></div>`;
}).join(""));
const inputWikitext = $row.find('.ill-input').text();
const match = inputWikitext.match(/\[\[(.*?)\]\]/);
if (match) {
const parts = match[1].split('|');
const targetPage = parts[0].trim();
const surfaceName = (parts[1] || parts[0]).trim();
const illLangs = topLinks.map(item => {
const isSameTitle = this.norm(item.title) === this.norm(targetPage);
return `|${item.lang}|${isSameTitle ? '' : item.title}`;
}).join("");
const ltParam = (this.norm(surfaceName) === this.norm(targetPage)) ? "" : `|lt=${surfaceName}`;
$outputCell.text(`{{ill|${targetPage}${ltParam}${illLangs}}}`);
}
$checkbox.prop('disabled', !topLinks.length);
if (!isManualToggling) {
$checkbox.prop('checked', !!topLinks.length);
}
this.updateOkButtonState($table);
},
onCheckbox(e) {
const $row = $(e.target).closest('tr');
const $radio = $row.find('input[type="radio"]:checked');
const isChecked = $(e.target).is(':checked');
if (isChecked) {
if ($radio.length) {
this.showSiteLinks({
target: $radio[0],
fromCheckbox: true
});
}
} else {
$row.find('.ill-sitelinks, .ill-output').empty();
}
},
onOkButton($editform, $wrapper, $textbox) {
let text = $textbox.textSelection('getContents');
let changed = false;
$wrapper.find('.ill-row-check:checked').each(function() {
const $row = $(this).closest('tr');
const input = $row.find('.ill-input').text();
const output = $row.find('.ill-output').text();
if (output) {
const escapedInput = input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
text = text.replace(new RegExp(escapedInput, "gi"), output);
changed = true;
}
});
if (changed) {
$('#wpSummary').val((i, v) => (v ? v + "; " : "") + this.Summary);
$textbox.textSelection('setContents', text);
$wrapper.remove();
$editform.show();
if (this.Config.AutoDiff) $('#wpDiff').click();
}
},
escapeHTML(str) {
return str.replace(/[&<>"']/g, m => ({
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
})[m]);
}
};
window.IllWill = IllWill;
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'jquery.textSelection'], () => {
$(() => IllWill.setup());
});
})();
//</nowiki>
mqu0dy07k04p0n2muko0gy1lsymjvie
739074
739072
2026-04-22T10:32:45Z
Sam Sailor
26820
Test
739074
javascript
text/javascript
//<nowiki>
(function() {
var Comrade = Comrade || {};
mw.util.addCSS(`
.ambox-Orphan { display: block !important; }
.comrade-container {
box-shadow: 0 1px 3px rgba(0,0,0,0.1); border-radius: 2px;
padding: 10px 15px; margin: 10px 0; display: flex;
flex-direction: column; align-items: flex-start; gap: 8px;
border: 1px solid #a2a9b1; background: #fcfcfc; line-height: 1.5;
}
.comrade-header { display: flex; align-items: center; justify-content: space-between; width: 100%; }
.comrade-content { display: flex; flex-direction: column; gap: 5px; width: 100%; }
.comrade-success { background-color: #d5f5e3 !important; border-left: 5px solid #27ae60 !important; color: #1b5e20 !important; }
.comrade-nudge { background: #fff9ea; border-left: 5px solid #f0ad4e; }
.comrade-info { background: #eaf3ff; border-left: 5px solid #36c; }
.comrade-status { background: #eaf3ff; border-left: 5px solid #36c; }
.comrade-container button:not(.comrade-dismiss) {
background: #f8f9fa; border: 1px solid #a2a9b1; border-radius: 2px;
padding: 2px 10px; cursor: pointer; font-weight: bold; font-size: 0.95em; margin-left: 10px;
}
.comrade-container button:not(.comrade-dismiss):hover { border-color: #36c; color: #36c; background: #fff; }
.comrade-dismiss {
background: transparent; border: none; color: #d33; font-weight: bold;
cursor: pointer; font-size: 0.9em;
}
.comrade-dismiss:hover { text-decoration: underline; }
.comrade-code-block { background: #f1f1f1; padding: 4px 8px; border-radius: 3px; font-family: monospace; display: inline-block; margin-top: 4px; }
`);
function appendDismiss($el) {
$('<button>').addClass('comrade-dismiss').text('Dismiss').click(function() {
$(this).closest('.comrade-container').fadeOut();
}).appendTo($el.find('.comrade-header'));
$("#siteSub").after($el);
}
async function checkAndRenderRedirect(redirTitle, targetTitle, rCategory, labelText, customWikitext) {
const api = new mw.Api();
const res = await api.get({
action: 'query',
titles: redirTitle,
formatversion: 2
});
if (res.query.pages[0].missing) {
const safeId = "comrade-redir-" + btoa(unescape(encodeURIComponent(redirTitle))).replace(/=/g, "");
const $container = $('<div>').addClass('comrade-container comrade-info').attr('id', safeId);
const $header = $('<div>').addClass('comrade-header');
const summary = `Created redirect from ${labelText.toLowerCase()} to [[${targetTitle}]]`;
const $btn = $('<button>').text('Create redirect').click(() => createModernRedirect(redirTitle, targetTitle, rCategory, summary, safeId, customWikitext));
$header.append($('<div>').append($('<strong>').text(`${labelText}: `), `Create redirect from `, $('<code>').text(redirTitle), $btn));
$container.append($header);
appendDismiss($container);
}
}
async function checkDomainRedirect(qid, currentTitle) {
let domainCandidate = "";
if (qid) {
try {
const res = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetclaims',
entity: qid,
property: 'P856',
format: 'json',
origin: '*'
}
});
if (res.claims?.P856) {
domainCandidate = res.claims.P856[0].mainsnak.datavalue.value;
}
} catch (e) {
console.log("Comrade: Wikidata API call failed.");
}
}
if (!domainCandidate) {
domainCandidate = $(".infobox .url a").last().attr("href") || $(".official-website a").first().attr("href");
}
if (domainCandidate) {
try {
const url = new URL(domainCandidate);
let domain = url.hostname.replace(/^www\d*\./, "");
if (domain.includes(".") && url.pathname === "/") {
const citationURL = '/api/rest_v1/data/citation/mediawiki/' + encodeURIComponent(domainCandidate);
$.ajax({
url: citationURL,
method: 'GET',
success: function() {
checkAndRenderRedirect(domain, currentTitle, "R from domain name", "Domain");
}
});
}
} catch (e) {
console.log("Comrade: Error parsing URL object.");
}
}
}
async function checkNameList(name, type, currentTitle, hasShortDesc, isOrphan) {
const api = new mw.Api();
const candidates = [`${name} (${type})`, `${name} (${type.toLowerCase()})`, `${name} (name)`, name];
const checked = new Set();
for (const title of candidates) {
if (checked.has(title)) continue;
checked.add(title);
const res = await api.get({
action: 'query',
prop: 'revisions',
titles: title,
rvprop: 'content',
formatversion: 2,
redirects: 0
});
const page = res.query.pages[0];
if (page && !page.missing) {
const text = page.revisions[0].content;
const resolvedTitle = page.title;
const isNameMatch = /\{\{(Surname|Hndis|Given[ _]name|Forename|Givenname)/i.test(text);
const isNameDab = /\{\{(Disambiguation|Dab|Disambig)(?:[^}]*\|(surname|surnames|given[ _]name|given[ _]names)|(?:\s*\}\}))/i.test(text);
if (isNameMatch || isNameDab) {
const escapedTitle = currentTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/ /g, '[ _]');
const alreadyListed = new RegExp('(\\[\\[|\\{\\{anbl\\|[ _]*|\\|)' + escapedTitle, 'i').test(text);
if (alreadyListed) break;
const $container = $('<div>').addClass('comrade-container');
const $header = $('<div>').addClass('comrade-header');
const $content = $('<div>').addClass('comrade-content');
if (isOrphan) {
$container.addClass('comrade-nudge');
$header.append($('<strong>').text('Comrade nudge:'));
} else {
$container.addClass('comrade-info');
$header.append($('<strong>').text('Informative:'));
}
$content.append($('<span>').append(`${type.charAt(0).toUpperCase() + type.slice(1)} not listed at `, $('<a>').attr({
href: mw.util.getUrl(resolvedTitle),
target: '_blank'
}).text(resolvedTitle), "; should it be?"));
const snippet = '* {{anbl|' + currentTitle + '}}';
const $snippetWrapper = $('<div>').css({
'display': 'flex',
'align-items': 'center',
'gap': '10px',
'margin-top': '4px'
});
const $instructionSpan = $('<span>').append('If the Short description is good, consider adding ', $('<code>').text(snippet).css({
'background-color': '#f8f9fa',
'border': '1px solid #eaecf0',
'border-radius': '2px',
'padding': '1px 4px'
}));
const $copyBtn = $('<button>').text('Copy').css('margin-left', '0').click(function() {
navigator.clipboard.writeText(snippet).then(() => {
const $this = $(this);
const originalText = $this.text();
$this.text('Copied!').prop('disabled', true);
setTimeout(() => {
$this.text(originalText).prop('disabled', false);
}, 1500);
});
});
$snippetWrapper.append($instructionSpan, $copyBtn);
$content.append($snippetWrapper);
if (!hasShortDesc) {
$content.append($('<span>').css({
'color': '#d33',
'font-weight': 'bold',
'margin-top': '5px'
}).text('⚠️ Missing short description: Please add a concise one before listing.'));
}
$container.append($header, $content);
appendDismiss($container);
break;
}
}
}
}
async function createModernRedirect(redirTitle, targetTitle, rCategory, customSummary, elementId, customWikitext) {
const api = new mw.Api();
const content = customWikitext || `#REDIRECT [[${targetTitle}]]\n\n{{Redirect category shell|\n{{${rCategory}}}\n}}`;
const summary = customSummary || `Created redirect from [[${redirTitle}]]`;
return api.postWithToken('csrf', {
action: 'edit',
title: redirTitle,
text: content,
summary: summary,
createonly: true
}).done(() => {
mw.notify("Redirect created!", {
type: 'success',
tag: 'comrade',
classes: ['comrade-success']
});
$(`#${elementId}`).fadeOut();
});
}
function getSmartSortKey(fullName, entity) {
const countryIds = entity.claims?.P27?.map(c => c.mainsnak?.datavalue?.value?.id) || [];
const locationIds = entity.claims?.P17?.map(c => c.mainsnak?.datavalue?.value?.id) || [];
const placeIds = [...(entity.claims?.P19 || []), ...(entity.claims?.P119 || [])].map(c => c.mainsnak?.datavalue?.value?.id).filter(Boolean);
const allRelevantIds = [...countryIds, ...locationIds, ...placeIds];
const birthYear = parseInt(entity.claims?.P569?.[0]?.mainsnak?.datavalue?.value?.time?.match(/[+-](\d{4})/)?.[1]) || 2026;
if (fullName.startsWith("Saint ")) return fullName.replace("Saint ", "");
const arabicParticles = /\b(Abu|Abd|Abdel|Abdul|ben|bin|bint)\b/i;
if (fullName.match(arabicParticles)) {
if (birthYear > 1900) {
const parts = fullName.split(' ');
const binIndex = parts.findIndex(p => p.toLowerCase() === 'bin' || p.toLowerCase() === 'ben');
if (binIndex > 0) return parts.slice(binIndex).join(' ') + ', ' + parts.slice(0, binIndex).join(' ');
} else {
return fullName;
}
}
const eastAsianQids = ["Q148", "Q184", "Q884", "Q424", "Q881", "Q17"];
if (allRelevantIds.some(id => eastAsianQids.includes(id))) {
if (allRelevantIds.includes("Q17") && birthYear > 1885) return westernSort(fullName);
const parts = fullName.split(' ');
if (parts.length > 1) return `${parts[0]}, ${parts.slice(1).join(' ')}`;
}
return westernSort(fullName);
}
function westernSort(name) {
let cleanName = name;
if (name.startsWith("O'")) cleanName = name.replace("O'", "O");
const parts = cleanName.split(' ');
if (parts.length < 2) return cleanName;
const surname = parts.pop();
if (["Jr.", "Jr", "III", "II", "Sr.", "Sr"].includes(surname)) {
const actualSurname = parts.pop();
return `${actualSurname}, ${parts.join(' ')} ${surname}`;
}
return `${surname}, ${parts.join(' ')}`;
}
async function performDeorphan() {
const api = new mw.Api();
await api.edit(mw.config.get('wgPageName'), function(rev) {
let text = rev.content;
const summary = 'Article has backlinks; removed [[Template:Orphan|{{Orphan}}]]';
text = text.replace(/\{\{Orphan\s*(?:\|[^}]*)?\}\}\s*/gi, '');
text = text.replace(/(\{\{Multiple[ _]issues\s*)\|\s*\|/gi, '$1|');
text = replaceMI(text);
text = text.replace(/\n{3,}/g, '\n\n').trim();
return {
text,
summary,
minor: true
};
});
mw.notify('Orphan tag removed!', {
type: 'success',
tag: 'comrade',
classes: ['comrade-success']
});
setTimeout(() => location.reload(), 700);
}
function replaceMI(text) {
const miStart = /\{\{Multiple[ _]issues\s*\|/gi;
let result = '';
let lastIndex = 0;
let match;
while ((match = miStart.exec(text)) !== null) {
const start = match.index;
result += text.slice(lastIndex, start);
let depth = 0;
let i = start;
while (i < text.length) {
if (text[i] === '{' && text[i + 1] === '{') {
depth++;
i += 2;
} else if (text[i] === '}' && text[i + 1] === '}') {
depth--;
i += 2;
if (depth === 0) break;
} else {
i++;
}
}
const end = i;
const full = text.slice(start, end);
const pipeIdx = full.indexOf('|');
const inner = full.slice(pipeIdx + 1, full.length - 2).trim();
const topLevel = extractTopLevelTemplates(inner);
if (topLevel.length === 0) {
result += '';
} else if (topLevel.length === 1) {
result += topLevel[0].trim();
} else {
result += full;
}
lastIndex = end;
miStart.lastIndex = lastIndex;
}
result += text.slice(lastIndex);
return result;
}
function extractTopLevelTemplates(inner) {
const templates = [];
let i = 0;
while (i < inner.length) {
if (inner[i] === '{' && inner[i + 1] === '{') {
let depth = 0;
const start = i;
while (i < inner.length) {
if (inner[i] === '{' && inner[i + 1] === '{') {
depth++;
i += 2;
} else if (inner[i] === '}' && inner[i + 1] === '}') {
depth--;
i += 2;
if (depth === 0) break;
} else {
i++;
}
}
templates.push(inner.slice(start, i));
} else {
i++;
}
}
return templates;
}
async function performSortCorrection(newName, type) {
const api = new mw.Api();
const title = mw.config.get("wgPageName");
try {
const data = await api.get({
action: 'query',
prop: 'revisions',
titles: title,
rvprop: 'content',
formatversion: 2
});
let content = data.query.pages[0].revisions[0].content;
const dsRegex = /\{\{(?:DEFAULTSORT|Defaultsort):[^}]+\}\}/i;
const newTag = `{{DEFAULTSORT:${newName}}}`;
let actionDesc = type === "add" ? "added" : "corrected";
if (dsRegex.test(content)) {
content = content.replace(dsRegex, newTag);
} else {
const catRegex = /\[\[Category:/i;
if (catRegex.test(content)) {
content = content.replace(catRegex, `${newTag}\n[[Category:`);
} else {
content += `\n\n${newTag}`;
}
}
await api.postWithToken('csrf', {
action: 'edit',
title: title,
text: content,
summary: `Setting DEFAULTSORT to ${newName}`,
nocreate: true
});
mw.notify(`DEFAULTSORT ${actionDesc} to ${newName}!`, {
title: 'Comrade Success',
type: 'success'
});
setTimeout(() => location.reload(), 700);
} catch (err) {
mw.notify('Failed to save DEFAULTSORT.', {
type: 'error'
});
}
}
function stripDiacritics(str) {
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
}
function renderSortNudge(target, reason) {
const $dsNudge = $('<div>').addClass('comrade-container comrade-nudge');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text(`Correct to: ${target}`).click(() => performSortCorrection(target, "format"));
$header.append($('<div>').append($('<strong>').text('DEFAULTSORT:'), ` ${reason} `, $btn));
$dsNudge.append($header);
appendDismiss($dsNudge);
}
async function fetchWikidataNameLabels(entity) {
const familyIds = entity.claims?.P734?.map(c => c.mainsnak.datavalue.value.id) || [];
const givenIds = entity.claims?.P735?.map(c => c.mainsnak.datavalue.value.id) || [];
const allNameIds = [...new Set([...familyIds, ...givenIds])];
if (allNameIds.length === 0) return {
givens: [],
surnames: []
};
const nameData = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetentities',
ids: allNameIds.join('|'),
props: 'labels',
languages: 'en',
format: 'json',
origin: '*'
},
dataType: 'json'
});
return {
givens: givenIds.map(id => nameData.entities[id]?.labels?.en?.value).filter(Boolean),
surnames: familyIds.map(id => nameData.entities[id]?.labels?.en?.value).filter(Boolean)
};
}
function fixNameCasing(part, referenceName) {
return part.split(' ').map(word => {
const match = referenceName.match(new RegExp(`\\b${word}\\b`, 'i'));
if (match) return match[0];
return referenceName.split(' ').find(tWord => word.toLowerCase().startsWith(tWord.toLowerCase()) || tWord.toLowerCase().startsWith(word.toLowerCase())) || word;
}).join(' ');
}
function getLongNameFromWikitext(wikitext) {
const firstLine = wikitext.split('\n').find(l => l.includes("'''"));
if (!firstLine) return null;
const boldMatch = firstLine.match(/'''(.+?)'''/);
if (!boldMatch) return null;
return boldMatch[1].replace(/\s*([“"«《„‹「『'].+?[”"»》”›」』']|\([^)\n]*\))\s*/g, ' ').replace(/\s+/g, ' ').trim();
}
if (mw.config.get("wgDBname") === "enwiki" && mw.config.get("wgUserId") === 19244234 && mw.config.get("wgNamespaceNumber") === 0 && mw.config.get("wgAction") === "view") {
$(document).ready(async function() {
const api = new mw.Api();
const qid = mw.config.get("wgWikibaseItemId");
try {
const [pageData, backlinkData] = await Promise.all([
api.get({
action: 'query',
prop: 'revisions',
titles: mw.config.get("wgPageName"),
rvprop: 'content',
formatversion: 2
}),
api.get({
action: 'query',
list: 'backlinks',
blfilterredir: 'nonredirects',
bllimit: 50,
blnamespace: 0,
bltitle: mw.config.get("wgPageName"),
formatversion: 2
})
]);
const page = pageData.query.pages[0];
if (!page || page.missing || !page.revisions) return;
const wikitext = page.revisions[0].content;
const currentTitle = mw.config.get("wgTitle");
const cleanTitle = stripDiacritics(currentTitle);
const isOrphanTagged = /\{\{\s*(Orphan|Do-attempt|Lonely|Orp|Orphaned article)/i.test(wikitext) || /\| *orphan *=/i.test(wikitext);
const backLinkCount = backlinkData.query.backlinks.length;
if (isOrphanTagged && backLinkCount >= 1) {
const $container = $('<div>').addClass('comrade-container comrade-status');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text('Remove orphan tag').click(() => performDeorphan());
$header.append($('<div>').append($('<strong>').text('Status:'), ` Article has ${backLinkCount} backlink(s).`, $btn));
$container.append($header);
appendDismiss($container);
}
if (cleanTitle !== currentTitle) checkAndRenderRedirect(cleanTitle, currentTitle, "R to diacritic", "Diacritic");
checkDomainRedirect(qid, currentTitle);
if (qid) {
const wikiData = await $.ajax({
url: "https://www.wikidata.org/w/api.php",
data: {
action: 'wbgetentities',
ids: qid,
props: 'claims|labels',
languages: 'en',
format: 'json',
origin: '*'
},
dataType: 'json'
});
const entity = wikiData.entities[qid];
const isHuman = entity?.claims?.P31?.some(c => c.mainsnak?.datavalue?.value?.id === "Q5");
const tClean = currentTitle.replace(/\s*\(.*?\)\s*/g, '').trim();
const nameParts = tClean.split(' ');
const hasFamilyNameHatnote = /\{\{family[ _]name[ _]hatnote/i.test(wikitext);
const hasShortDesc = /\{\{\s*[Ss]hort description/i.test(wikitext);
const dsMatch = wikitext.match(/\{\{(?:DEFAULTSORT|Defaultsort):([^}]+)\}\}/i);
const isOrphan = isOrphanTagged && backLinkCount < 1;
const primarySurname = hasFamilyNameHatnote ? nameParts[0] : (getSmartSortKey(tClean, entity).split(',')[0] || nameParts[nameParts.length - 1]);
let targetSortName = "";
if (isHuman) {
const {
givens,
surnames
} = await fetchWikidataNameLabels(entity);
const refName = entity.labels?.en?.value || currentTitle;
if (dsMatch) {
const currentDS = dsMatch[1].trim();
targetSortName = currentDS;
if (currentDS.includes(',')) {
let [lastPart, firstPart] = currentDS.split(',').map(s => s.trim());
if (firstPart.toLowerCase().endsWith(lastPart.toLowerCase())) {
firstPart = firstPart.substring(0, firstPart.length - lastPart.length).trim();
targetSortName = `${fixNameCasing(lastPart, refName)}, ${fixNameCasing(firstPart, refName)}`;
renderSortNudge(targetSortName, "Redundant surname in given name field.");
} else if (givens.length > 0 && lastPart.split(' ').length > 1) {
const misplacedGiven = lastPart.split(' ').find(part => givens.some(gn => gn.toLowerCase() === part.toLowerCase()));
if (misplacedGiven) {
const newSurname = lastPart.split(' ').filter(p => p !== misplacedGiven).join(' ');
targetSortName = `${fixNameCasing(newSurname, refName)}, ${fixNameCasing(misplacedGiven + ' ' + firstPart, refName)}`;
renderSortNudge(targetSortName, `Wikidata suggests "${misplacedGiven}" is a given name.`);
}
}
}
} else {
targetSortName = hasFamilyNameHatnote ? `${nameParts[0]}, ${nameParts.slice(1).join(' ')}` : getSmartSortKey(tClean, entity);
const $dsMissing = $('<div>').addClass('comrade-container comrade-nudge');
const $header = $('<div>').addClass('comrade-header');
const $btn = $('<button>').text(`Add: {{DEFAULTSORT:${targetSortName}}}`).click(() => performSortCorrection(targetSortName, "add"));
$header.append($('<div>').append($('<strong>').text('DEFAULTSORT:'), ` Tag is missing. `, $btn));
$dsMissing.append($header);
appendDismiss($dsMissing);
}
const longName = getLongNameFromWikitext(wikitext);
if (longName && longName.length > tClean.length) {
let sortKey = hasFamilyNameHatnote ? `${longName.split(' ')[0]}, ${longName.split(' ').slice(1).join(' ')}` : getSmartSortKey(longName, entity);
const rLongWikitext = `#REDIRECT [[${currentTitle}]]\n\n{{Redirect category shell|\n{{R from long name}}\n}}\n{{DEFAULTSORT:${sortKey}}}`;
checkAndRenderRedirect(longName, currentTitle, null, "Long name", rLongWikitext);
}
if (targetSortName) {
let targetPage = currentTitle.includes(' (') ? currentTitle.split(' (')[0] : currentTitle;
let rCat = targetPage !== currentTitle ? "R from ambiguous sort name" : "R from sort name";
if (targetSortName.includes(',')) {
const p = targetSortName.split(',');
rCat += `|${p[0].trim().charAt(0).toUpperCase()}|${p[1].trim().charAt(0).toUpperCase()}`;
}
checkAndRenderRedirect(targetSortName, targetPage, rCat, "Sort name");
}
const finalFamilyNames = new Set();
const finalGivenNames = new Set();
surnames.forEach(fn => {
if (tClean.toLowerCase().includes(fn.toLowerCase())) {
fn.split(' ').forEach(part => finalFamilyNames.add(part));
finalFamilyNames.add(fn);
}
});
givens.forEach(gn => {
if (tClean.toLowerCase().includes(gn.toLowerCase())) finalGivenNames.add(gn);
});
if (finalFamilyNames.size === 0 && nameParts.length >= 2) finalFamilyNames.add(nameParts[nameParts.length - 1]);
if (finalGivenNames.size === 0 && nameParts.length >= 2) finalGivenNames.add(nameParts[0]);
const getPriorityTarget = async (name, type) => {
const titles = type === 'surname' ? [`List of people with surname ${name}`, `List of people with the English surname ${name}`, `${name} (surname)`] : [`List of people with given name ${name}`, `${name} (given name)`];
const res = await api.get({
action: 'query',
titles: titles.join('|'),
formatversion: 2
});
return titles.find(t => res.query.pages.find(pg => pg.title === t && !pg.missing));
};
for (const name of finalGivenNames) {
if (name.toLowerCase() === primarySurname.toLowerCase()) continue;
const target = await getPriorityTarget(name, 'given');
if (target) await checkNameList(name, 'given name', currentTitle, hasShortDesc, isOrphan, target);
}
for (const name of finalFamilyNames) {
const target = await getPriorityTarget(name, 'surname');
if (target) await checkNameList(name, 'surname', currentTitle, hasShortDesc, isOrphan, target);
}
}
}
} catch (err) {
console.error("Comrade error:", err);
}
});
}
})();
(function() {
if (window.IllWill) return;
const STRIKEOUT = true;
const HIDE = 'hide';
const IllWill = {
Version: "1.1.1",
Attribution: 'created by <a target="_blank" href="/wiki/User:Cobaltcigs">cobaltcigs</a>',
Summary: "Added interlanguage links using IllWill.js",
DataApi: "https://www.wikidata.org/w/api.php",
LocalLang: mw.config.get("wgPageContentLanguage"),
Disambiguator: / \(.*\)$/,
SiteLinks: {},
Config: {
MaxLanguages: 5,
IgnoreScientific: STRIKEOUT,
AutoDiff: true
},
css: `
#illwill { clear: both; margin-bottom: 1.5em; border: 1px solid #a2a9b1; padding: 15px; background: #f8f9fa; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.ill-sitelinks a { display: inline-block; margin-bottom: 3px; font-size: 0.9em; color: #36c; }
.ill-size-tag { font-size: 0.8em; color: #72777d; margin-left: 6px; white-space: nowrap; }
.illwill-ignore { text-decoration: line-through; color: #72777d; font-size: 0.85em; }
.ill-x { width: 35px; text-align: center; }
.ill-input, .ill-output { font-family: 'Courier New', monospace; white-space: pre-wrap; font-size: 0.9em; background: #fff; border: 1px solid #eaecf0; padding: 4px; border-radius: 2px; }
#ill-buttons { text-align: right; padding: 10px; border-top: 1px solid #a2a9b1; margin-top: 10px; }
#ill-buttons button { margin-left: 10px; cursor: pointer; padding: 6px 16px; border-radius: 2px; border: 1px solid #a2a9b1; transition: all 0.2s; }
#ill-ok { background: #36c; color: #fff; border-color: #36c !important; font-weight: bold; }
#ill-ok:hover { background: #447ff5; }
#ill-ok:disabled { background: #eaecf0; color: #72777d; border-color: #c8ccd1 !important; cursor: not-allowed; opacity: 0.7; }
#ill-table { width: 100%; border-collapse: collapse; margin-top: 10px; background: white; }
#ill-table th { background: #f2f2f2; text-align: left; padding: 8px; border: 1px solid #a2a9b1; }
#ill-table td { padding: 8px; border: 1px solid #a2a9b1; vertical-align: top; }
#ill-table caption { background: #eaffea; font-weight: bold; padding: 10px; border: 1px solid #a2a9b1; border-bottom: none; font-size: 1.1em; }
#ill-help { font-size: 0.85em; color: #54595d; padding: 10px; line-height: 1.4; }
.ill-loading-inline { font-style: italic; color: #72777d; font-size: 0.9em; display: block; margin: 5px 0; }
`,
setup() {
if (!["edit", "submit"].includes(mw.config.get('wgAction'))) return;
mw.loader.addStyleTag(this.css);
const link = mw.util.addPortletLink('p-cactions', '#', 'IllWill', 'ca-illwill', "Add interlanguage links via Wikidata");
if (link) {
link.addEventListener('click', (e) => {
e.preventDefault();
this.makePanel();
});
}
},
norm(s) {
if (!s) return "";
const t = s.trim().replace(/[\s_]+/g, " ");
return t.charAt(0).toUpperCase() + t.slice(1);
},
async getRedLinks($textbox) {
const txt = $textbox.textSelection('getContents') || "";
const wl = txt.match(/\[\[[^[\]\n]+\]\]/g);
if (!wl) return {
reds: [],
txt
};
const api = new mw.Api();
const textToParse = wl.join("").replace(/\|[^\]]+/g, "");
try {
const data = await api.post({
action: 'parse',
text: textToParse,
contentmodel: 'wikitext',
formatversion: 2
});
const tempDiv = document.createElement('div');
tempDiv.innerHTML = data.parse.text;
const reds = Array.from(tempDiv.querySelectorAll("a.new")).map(x => {
const urlParams = new URLSearchParams(x.href.split('?')[1]);
return urlParams.get('title')?.replace(/_/g, " ");
}).filter(Boolean);
return {
reds: [...new Set(reds)],
txt
};
} catch (e) {
return {
reds: [],
txt
};
}
},
async makePanel() {
const $editform = $('#editform');
const $textbox = $('#wpTextbox1');
$('#illwill').remove();
$editform.hide();
const $wrapper = $('<div id="illwill">Loading redlinks...</div>').insertBefore($editform);
const {
reds,
txt
} = await this.getRedLinks($textbox);
const wtLinks = [];
reds.forEach(title => {
const escaped = title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\s/g, "[\\s_]+");
const re = new RegExp("\\[\\[\\s*" + escaped + "\\s*(?:\\|[^\\[\\]]+)?\\]\\]", "ig");
const match = txt.match(re);
if (match && !wtLinks.some(z => z.title === title)) {
wtLinks.push({
link: match[0],
title
});
}
});
if (!wtLinks.length) {
const $back = $('<button>Quit</button>').on('click', () => {
$wrapper.remove();
$editform.show();
});
$wrapper.empty().append($('<strong>No valid redlinks found in text.</strong> '), $back);
return;
}
const $table = $(`
<table id="ill-table" class="wikitable">
<caption>IllWill.js (v${this.Version})</caption>
<thead>
<tr>
<th class="ill-x"><input type="checkbox" id="ill-select-all" disabled></th>
<th>Input link</th>
<th>Possible Wikidata matches</th>
<th>Sitelinks (top ${this.Config.MaxLanguages} by size)</th>
<th>Resulting wikitext</th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<td colspan="3" id="ill-help">Carefully examine possible Wikidata matches.</td>
<td colspan="2" id="ill-buttons">
<button id="ill-cancel">Cancel</button>
<button id="ill-ok" disabled>Show changes</button>
</td>
</tr>
</tfoot>
</table>
`);
const $tbody = $table.find('tbody');
wtLinks.forEach((item, i) => {
$tbody.append(`
<tr id="ill-row-${i}" data-index="${i}">
<td class="ill-x"><input type="checkbox" class="ill-row-check" disabled></td>
<td class="ill-input">${this.escapeHTML(item.link)}</td>
<td class="ill-wd-item"><span class="ill-loading-inline">Searching...</span></td>
<td class="ill-sitelinks"></td>
<td class="ill-output"></td>
</tr>
`);
});
$wrapper.empty().append($table);
const chunkSize = 5;
for (let i = 0; i < wtLinks.length; i += chunkSize) {
const chunk = wtLinks.slice(i, i + chunkSize);
await Promise.all(chunk.map((item, idx) => this.fetchWikidataOptimized(item.title, i + idx)));
}
$table.on('click', '#ill-cancel', () => {
$editform.show();
$wrapper.remove();
});
$table.on('click', '#ill-ok', () => this.onOkButton($editform, $wrapper, $textbox));
$table.on('change', '#ill-select-all', (e) => {
const isChecked = $(e.target).is(':checked');
$table.find('.ill-row-check:not(:disabled)').each(function() {
$(this).prop('checked', isChecked).trigger('change');
});
});
$table.on('change', '.ill-row-check', (e) => {
this.onCheckbox(e);
this.updateOkButtonState($table);
});
$table.on('change', 'input[type="radio"]', (e) => {
this.showSiteLinks(e);
});
},
async fetchWikidataOptimized(title, index) {
const query = title.replace(this.Disambiguator, "");
const url = `${this.DataApi}?action=wbsearchentities&search=${encodeURIComponent(query)}&language=${this.LocalLang}&limit=5&format=json&origin=*`;
try {
const resp = await fetch(url);
const data = await resp.json();
const results = data.search;
const $cell = $(`#ill-row-${index} .ill-wd-item`);
if (!results || !results.length) {
$cell.html('<span class="ill-na">(no results)</span>');
return;
}
const entityIds = results.map(r => r.id);
await this.fetchEntities(entityIds, index, $cell);
} catch (e) {
$(`#ill-row-${index} .ill-wd-item`).text("Search failed.");
}
},
async fetchEntities(ids, rowIndex, $cell) {
const url = `${this.DataApi}?action=wbgetentities&ids=${ids.join('|')}&props=labels|descriptions|claims|sitelinks&format=json&origin=*`;
try {
const resp = await fetch(url);
const data = await resp.json();
$cell.empty();
ids.forEach(qNum => {
const entity = data.entities[qNum];
if (!entity) return;
const isScientific = entity.claims?.P31?.some(s => s.mainsnak?.datavalue?.value?.id === "Q13442814");
if (isScientific && this.Config.IgnoreScientific === HIDE) return;
const desc = entity.descriptions?.[this.LocalLang]?.value || entity.descriptions?.en?.value || "";
const label = entity.labels?.[this.LocalLang]?.value || entity.labels?.en?.value || qNum;
const isStrikeout = isScientific && this.Config.IgnoreScientific === STRIKEOUT;
const $div = $(`
<div class="${isStrikeout ? 'illwill-ignore' : ''}">
<input type="radio" name="selection-${rowIndex}" value="${qNum}" id="radio-${qNum}" ${isStrikeout ? 'disabled' : ''}>
<label for="radio-${qNum}">
<a href="https://wikidata.org/wiki/${qNum}" target="_blank">${this.escapeHTML(label)}</a>:
<span class="ill-desc">${desc ? this.escapeHTML(desc) : '<i>no description</i>'}</span>
</label>
</div>
`);
$cell.append($div);
this.SiteLinks[qNum] = entity.sitelinks || {};
});
} catch (e) {
$cell.text("Detail fetch failed.");
}
},
updateOkButtonState($table) {
const $rows = $table.find('.ill-row-check:not(:disabled)');
const $checkedRows = $rows.filter(':checked');
const anyEnabled = $rows.length > 0;
$table.find('#ill-ok').prop('disabled', $checkedRows.length === 0);
const $master = $table.find('#ill-select-all');
$master.prop('disabled', !anyEnabled);
if (anyEnabled && $rows.length === $checkedRows.length) {
$master.prop('checked', true);
} else {
$master.prop('checked', false);
}
},
async showSiteLinks(e) {
const isManualToggling = e.fromCheckbox || false;
const qNum = e.target.value;
const $row = $(e.target).closest('tr');
const sitelinks = this.SiteLinks[qNum];
const $checkbox = $row.find('.ill-row-check');
const $sitelinkCell = $row.find('.ill-sitelinks');
const $outputCell = $row.find('.ill-output');
const $table = $('#ill-table');
const effectiveMax = Math.min(Math.max(this.Config.MaxLanguages, 3), 6);
const validKeys = Object.keys(sitelinks).filter(k => k.endsWith('wiki') && !k.includes('commons'));
$sitelinkCell.html('<span class="ill-loading-inline">Measuring articles...</span>');
$outputCell.empty();
if (!isManualToggling) {
$checkbox.prop('disabled', true).prop('checked', false);
}
const candidates = validKeys.slice(0, 25);
const sizeMap = [];
await Promise.all(candidates.map(async (key) => {
const lang = key.replace('wiki', '').replace(/_/g, '-');
const title = sitelinks[key].title;
const endpoint = `https://${lang}.wikipedia.org/w/api.php?action=query&titles=${encodeURIComponent(title)}&prop=info&format=json&origin=*`;
try {
const res = await fetch(endpoint).then(r => r.json());
const page = Object.values(res.query.pages)[0];
sizeMap.push({
lang,
title,
length: page.length || 0
});
} catch (err) {
sizeMap.push({
lang,
title,
length: 0
});
}
}));
sizeMap.sort((a, b) => b.length - a.length);
const topLinks = sizeMap.slice(0, effectiveMax);
$sitelinkCell.html(topLinks.map(item => {
const kbValue = Math.round(item.length / 1024);
return `<div><a href="https://${item.lang}.wikipedia.org/wiki/${encodeURIComponent(item.title)}" target="_blank">${item.lang}:${this.escapeHTML(item.title)}</a> <span class="ill-size-tag">(${kbValue} kB)</span></div>`;
}).join(""));
const inputWikitext = $row.find('.ill-input').text();
const match = inputWikitext.match(/\[\[(.*?)\]\]/);
if (match) {
const parts = match[1].split('|');
const targetPage = parts[0].trim();
const surfaceName = (parts[1] || parts[0]).trim();
const illLangs = topLinks.map(item => {
const isSameTitle = this.norm(item.title) === this.norm(targetPage);
return `|${item.lang}|${isSameTitle ? '' : item.title}`;
}).join("");
const ltParam = (this.norm(surfaceName) === this.norm(targetPage)) ? "" : `|lt=${surfaceName}`;
$outputCell.text(`{{ill|${targetPage}${ltParam}${illLangs}}}`);
}
$checkbox.prop('disabled', !topLinks.length);
if (!isManualToggling) {
$checkbox.prop('checked', !!topLinks.length);
}
this.updateOkButtonState($table);
},
onCheckbox(e) {
const $row = $(e.target).closest('tr');
const $radio = $row.find('input[type="radio"]:checked');
const isChecked = $(e.target).is(':checked');
if (isChecked) {
if ($radio.length) {
this.showSiteLinks({
target: $radio[0],
fromCheckbox: true
});
}
} else {
$row.find('.ill-sitelinks, .ill-output').empty();
}
},
onOkButton($editform, $wrapper, $textbox) {
let text = $textbox.textSelection('getContents');
let changed = false;
$wrapper.find('.ill-row-check:checked').each(function() {
const $row = $(this).closest('tr');
const input = $row.find('.ill-input').text();
const output = $row.find('.ill-output').text();
if (output) {
const escapedInput = input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
text = text.replace(new RegExp(escapedInput, "gi"), output);
changed = true;
}
});
if (changed) {
$('#wpSummary').val((i, v) => (v ? v + "; " : "") + this.Summary);
$textbox.textSelection('setContents', text);
$wrapper.remove();
$editform.show();
if (this.Config.AutoDiff) $('#wpDiff').click();
}
},
escapeHTML(str) {
return str.replace(/[&<>"']/g, m => ({
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
})[m]);
}
};
window.IllWill = IllWill;
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'jquery.textSelection'], () => {
$(() => IllWill.setup());
});
})();
(function() {
'use strict';
const config = {
enableNS14: true,
rootWhitelists: ['Materials'],
userWhitelist: ""
};
const categoryDataCache = {};
let scanProgress = 0;
let totalToScan = 0;
async function verifyTreeMembership() {
if (!config.enableNS14) return false;
const api = new mw.Api();
const currentTitle = mw.config.get("wgPageName").replace(/_/g, ' ');
const fullRoots = config.rootWhitelists.concat(config.userWhitelist.split(',').map(s => s.trim()).filter(s => s)).map(cat => (cat.startsWith('Category:') ? cat : `Category:${cat}`));
if (fullRoots.includes(currentTitle)) return true;
let categoriesToTable = mw.config.get('wgCategories') || [];
let checked = new Set();
while (categoriesToTable.length > 0) {
if (categoriesToTable.some(cat => fullRoots.includes('Category:' + cat))) {
return true;
}
categoriesToTable.forEach(cat => checked.add(cat));
try {
const res = await api.get({
formatversion: 2,
prop: 'categories',
titles: categoriesToTable.map(c => 'Category:' + c),
cllimit: 'max'
});
let nextBatch = [];
if (res.query && res.query.pages) {
res.query.pages.forEach(page => {
if (page.categories) {
page.categories.forEach(parent => {
const parentName = parent.title.substring('Category:'.length);
if (!checked.has(parentName)) {
nextBatch.push(parentName);
}
});
}
});
}
categoriesToTable = [...new Set(nextBatch)];
} catch (e) {
return false;
}
}
return false;
}
function getChemboxBlock(wikitext) {
const chemboxRegex = /\{\{\s*Chembox[\s_]*(?:[^{}]|\{\{(?:[^{}]|\{\{[^{}]*\}\})*\}\})*\}\}/i;
const match = wikitext.match(chemboxRegex);
return match ? match[0] : null;
}
function getFormulaFromWikitext(wikitext) {
const chembox = getChemboxBlock(wikitext);
if (!chembox) return null;
const formulaMatch = chembox.match(/\|\s*Formula\s*=\s*([^|\n}]+)/i);
if (formulaMatch) {
let rawFormula = formulaMatch[1].replace(/<\/?sub>/gi, "").trim();
rawFormula = rawFormula.split(/\s+/)[0];
return rawFormula.split(',')[0].trim();
}
const propRegex = /\{\{\s*Chembox[\s_]+Properties(?:[^{}]|\{\{(?:[^{}]|\{\{[^{}]*\}\})*\}\})*\}\}/i;
const propSection = chembox.match(propRegex);
if (propSection) {
const content = propSection[0];
const allElements = ['Ac', 'Ag', 'Al', 'Am', 'Ar', 'As', 'At', 'Au', 'B', 'Ba', 'Be', 'Bh', 'Bi', 'Bk', 'Br', 'C', 'Ca', 'Cd', 'Ce', 'Cf', 'Cl', 'Cm', 'Cn', 'Co', 'Cr', 'Cs', 'Cu', 'D', 'Db', 'Ds', 'Dy', 'Er', 'Es', 'Eu', 'F', 'Fe', 'Fl', 'Fm', 'Fr', 'Ga', 'Gd', 'Ge', 'H', 'He', 'Hf', 'Hg', 'Ho', 'Hs', 'I', 'In', 'Ir', 'K', 'Kr', 'La', 'Li', 'Lr', 'Lu', 'Lv', 'Mc', 'Md', 'Mg', 'Mn', 'Mo', 'Mt', 'N', 'Na', 'Nb', 'Nd', 'Ne', 'Nh', 'Ni', 'No', 'Np', 'O', 'Og', 'Os', 'P', 'Pa', 'Pb', 'Pd', 'Pm', 'Po', 'Pr', 'Pt', 'Pu', 'Ra', 'Rb', 'Re', 'Rf', 'Rg', 'Rh', 'Rn', 'Ru', 'S', 'Sb', 'Sc', 'Se', 'Sg', 'Si', 'Sm', 'Sn', 'Sr', 'Ta', 'Tb', 'Tc', 'Te', 'Th', 'Ti', 'Tl', 'Tm', 'Ts', 'U', 'V', 'W', 'Xe', 'Y', 'Yb', 'Zn', 'Zr'];
let data = {};
allElements.forEach(el => {
const elRegex = new RegExp(`\\|\\s*${el}\\s*=\\s*(\\d*\\.?\\d+)`, 'i');
const elMatch = content.match(elRegex);
if (elMatch && elMatch[1] !== "0") {
data[el] = elMatch[1] === "1" ? "" : elMatch[1];
}
});
const elementsPresent = Object.keys(data);
if (elementsPresent.length > 0) {
let formulaArray = [];
const hasC = "C" in data;
if (hasC) {
formulaArray.push("C" + data["C"]);
if ("H" in data) formulaArray.push("H" + data["H"]);
}
elementsPresent.filter(el => !(hasC && (el === "C" || el === "H"))).sort().forEach(el => {
formulaArray.push(el + data[el]);
});
return formulaArray.join('');
}
}
return null;
}
function getCASFromWikitext(wikitext) {
const chembox = getChemboxBlock(wikitext);
if (!chembox) return null;
const idRegex = /\{\{\s*Chembox[\s_]+Identifiers(?:[^{}]|\{\{(?:[^{}]|\{\{[^{}]*\}\})*\}\})*\}\}/i;
const idSection = chembox.match(idRegex);
if (!idSection) return null;
const content = idSection[0];
const nbsp = String.fromCharCode(160);
const casMatch = content.match(/\|\s*CASNo\d*\b(?!\s*_)\s*=\s*([^|}\n]+)/i);
if (casMatch) {
let val = casMatch[1].replace(/\{\{[^}]+\}\}/g, "").replace(/\u00AD/g, "").split(" ").join("");
while (val.indexOf(nbsp) !== -1) val = val.replace(nbsp, "");
while (val.indexOf(" ") !== -1) val = val.replace(" ", "");
const cleanedCas = val.trim();
return /^\d{2,7}-\d{2}-\d$/.test(cleanedCas) ? cleanedCas : null;
}
return null;
}
async function getWikidataValue() {
const qid = mw.config.get('wgWikidataItemId');
if (!qid) return null;
const api = new mw.ForeignApi('https://www.wikidata.org/w/api.php');
const res = await api.get({
action: 'wbgetentities',
ids: qid,
props: 'claims',
format: 'json',
formatversion: 2
});
const claims = res.entities[qid].claims;
const data = {};
if (claims.P274) data.formula = claims.P274[0].mainsnak.datavalue.value;
if (claims.P231) data.cas = claims.P231[0].mainsnak.datavalue.value;
return data;
}
async function startBackgroundCategoryScan() {
const api = new mw.Api();
const titles = $('#mw-pages li a').map((i, el) => $(el).attr('title') || $(el).text()).get();
totalToScan = titles.length;
if (totalToScan === 0) return;
const chunks = arrayChunk(titles, 50);
for (const chunk of chunks) {
const res = await api.get({
action: 'query',
prop: 'revisions',
titles: chunk,
rvprop: 'content',
formatversion: 2
});
if (res.query && res.query.pages) {
res.query.pages.forEach(page => {
if (page.missing || !page.revisions) return;
const wikitext = page.revisions[0].content;
const f = getFormulaFromWikitext(wikitext);
const c = getCASFromWikitext(wikitext);
if (f || c) categoryDataCache[page.title] = {
formula: f,
cas: c
};
});
}
scanProgress += chunk.length;
$('#caschemassist-cat-btn').text(`Scanning... ${scanProgress}/${totalToScan}`);
}
$('#caschemassist-cat-btn').text('Show missing CAS/Chem').prop('disabled', false);
}
async function renderCategoryAudit() {
const api = new mw.Api();
const missingCheckTitles = [];
Object.values(categoryDataCache).forEach(data => {
if (data.formula) missingCheckTitles.push(data.formula);
if (data.cas) missingCheckTitles.push(data.cas);
});
if (missingCheckTitles.length === 0) {
mw.notify("No valid Chembox data found in this category.");
return;
}
const existenceMap = {};
const chunks = arrayChunk([...new Set(missingCheckTitles)], 50);
for (const chunk of chunks) {
const res = await api.get({
action: 'query',
titles: chunk,
formatversion: 2
});
if (res.query && res.query.pages) {
res.query.pages.forEach(p => {
existenceMap[p.title] = !p.missing;
});
}
}
$('#mw-pages li').each(function() {
const title = $(this).find('a').attr('title') || $(this).find('a').text().trim();
const data = categoryDataCache[title];
if (!data) return;
if (data.formula && !existenceMap[data.formula]) {
$(this).append($('<span>').css({
'color': 'red',
'font-size': '0.85em',
'margin-left': '8px',
'font-weight': 'bold'
}).text(` [${data.formula}]`));
}
if (data.cas && !existenceMap[data.cas]) {
$(this).append($('<span>').css({
'color': 'darkred',
'font-size': '0.85em',
'margin-left': '8px',
'font-weight': 'bold'
}).text(` [CAS: ${data.cas}]`));
}
});
$('#caschemassist-cat-btn').text('Audit complete').prop('disabled', true);
}
async function handleSingleArticle() {
const api = new mw.Api();
const currentTitle = mw.config.get("wgTitle");
const [wikiRes, wikidata] = await Promise.all([
api.get({
action: 'query',
prop: 'revisions',
titles: mw.config.get("wgPageName"),
rvprop: 'content',
formatversion: 2
}),
getWikidataValue()
]);
const page = wikiRes.query.pages[0];
if (!page || page.missing || !page.revisions) return;
const wikitext = page.revisions[0].content;
const formula = getFormulaFromWikitext(wikitext);
const cas = getCASFromWikitext(wikitext);
if (wikidata) {
let mismatches = [];
if (formula && wikidata.formula && formula !== wikidata.formula) {
mismatches.push(`Formula mismatch: Chembox "${formula}" vs Wikidata "${wikidata.formula}"`);
}
if (cas && wikidata.cas && cas !== wikidata.cas) {
mismatches.push(`CAS mismatch: Chembox "${cas}" vs Wikidata "${wikidata.cas}"`);
}
if (mismatches.length > 0) {
const $warning = $('<div>').css({
'background': '#fff2f2',
'border': '2px solid #d33',
'padding': '10px',
'margin-bottom': '1em',
'color': '#b32424'
}).append($('<strong>').text('⚠️ Data Mismatch Detected: '), mismatches.join(' | '));
$('#mw-content-text').prepend($warning);
}
}
async function checkAndRender(target, label, customWikitext) {
const check = await api.get({
action: 'query',
titles: target,
formatversion: 2
});
if (check.query.pages[0].missing) {
const $container = $('<div>').css({
'background': '#f8f9fa',
'border': '1px solid #a2a9b1',
'padding': '10px',
'margin-bottom': '1em'
});
const $btn = $('<button>').text(`Create redirect: ${target}`).click(async function() {
await api.postWithEditToken({
action: 'edit',
title: target,
text: customWikitext,
summary: `Creating redirect to [[${currentTitle}]] (${label})`,
createonly: true
});
mw.notify(`Created redirect: ${target}`);
$container.fadeOut(400, function() {
$(this).remove();
});
});
$container.append($('<strong>').text(`${label}: `), `Missing redirect. `, $btn);
$('#mw-content-text').prepend($container);
}
}
if (formula && formula !== currentTitle) {
const formulaWikitext = `#REDIRECT [[${currentTitle}]]\n\n{{Redirect category shell|\n{{R from chemical formula}}\n}}`;
await checkAndRender(formula, "Chemical formula", formulaWikitext);
}
if (cas && cas !== currentTitle) {
const casWikitext = `#REDIRECT [[${currentTitle}]]\n\n{{Redirect category shell|\n{{R from CAS Registry Number}}\n{{R unprintworthy}}\n}}`;
await checkAndRender(cas, "CAS number", casWikitext);
}
}
function arrayChunk(arr, size) {
const result = [];
for (let i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
}
$(document).ready(async function() {
const ns = mw.config.get("wgNamespaceNumber");
if (ns === 14) {
if (await verifyTreeMembership()) {
const $catContainer = $('#mw-pages');
if ($catContainer.length) {
const $btn = $('<button>').text('Initializing scan...').attr('id', 'caschemassist-cat-btn').addClass('mw-ui-button').css({
'margin': '0 10px 10px 0',
'float': 'right',
'position': 'relative',
'z-index': '100'
}).prop('disabled', true).click(renderCategoryAudit);
$catContainer.prepend($btn);
startBackgroundCategoryScan();
}
}
} else if (ns === 0 && mw.config.get("wgAction") === "view") {
handleSingleArticle();
}
});
})();
//</nowiki>
fvdmuvcp8lo9pv3ug2btz3g6wf5gi30
Talk:.gb
1
104686
739035
735287
2026-04-21T15:56:59Z
MPostoronca-WMF
72719
testing
739035
wikitext
text/x-wiki
{{DYKtalk|2019年|6月1日}}
this is an edit
test edit
{{ DYKEntry/archive
| nominator = Rowingbohe
| image =
| question = [[英国]]因为使用[[.uk]]域名而'''[[.gb|废弃了哪一个]]'''[[互联网]][[国家代码顶级域名]]?
| timestamp = 1558911236
| author = Rowingbohe
| type = technology
| article = .gb
| hash = 05339082877c77c30c6dd066bba13d76260b0b02
| result = +|05339082877c77c30c6dd066bba13d76260b0b02|1559332225
| revid = 395647
| closets = 1559371219
}}
**{{支持}}:符合标准。--[[User:Masdggg|<span style="color: RED;">'''風雲北洋'''</span>]]※[[User talk:Masdggg|Talk]] 2019年5月27日 (一) 13:12 (UTC)
**{{支持}}:符合标准。[[User:Walter Grassroot|🌜山西特产批发零售™️🌽🌶️🍎🍠🐓🐐]]([[User talk:Walter Grassroot|留言]]) 2019年5月28日 (二) 05:46 (UTC)
**{{支持}}:符合标准。--[[User:Unravel17|Unravel17]]([[User talk:Unravel17|留言]]) 2019年5月28日 (二) 07:15 (UTC)
**{{支持}},符合标准。--[[Special:Contribs/Humbleblue|HB]] 2019年5月28日 (二) 11:12 (UTC)
**{{支持}},符合标准。--[[U:SH6188 |<span style="font:100% Times;white-space:nowrap;border-radius:99em;padding:0 4em;box-shadow:2px 2px 11px #bbb;background:#EDA456;color:#9C0000 " lang="-en">KMB-ATENU139 </span>]] ([[UT:SH6188|討論]]) 2019年5月28日 (二) 12:18 (UTC)
**{{支持}}:符合标准,很有趣的一个条目,说明国码域名和[[ISO 3166-1]]不等同。--[[User:dqwyy|dqwyy]] ([[User_talk:dqwyy|talk]]) <sub>落忆归海枫音乡</sub> 2019年5月29日 (三) 04:25 (UTC)
**{{反對}}:正文不足400字,以參考資料充位元組,dyk標準原來降到這麼低--[[User:Doitagainagain|Doitagainagain]]([[User talk:Doitagainagain|留言]]) 2019年5月30日 (四) 18:28 (UTC)
***D17同学,想报复也找个合理点的理由好的吧,DYKC没有规定参考资料字数要减去,只规定字节数大于3000。请你提高知识水平再来评审。{{mute|Doitagainagain}}--<span style="font: 14px Comic Sans MS;text-shadow:0 1px 5px #66f9cf;">[[U:Rowingbohe|Rowing]][[UT:Rowingbohe|bohe♬~]]</span><small>欢迎关注近期[[WP:GAC|优良条目评选]] [[WP:TZ|Taichow]]/[[U:Rowingbohe/访客签名|Sign]]</small> 2019年5月30日 (四) 22:44 (UTC)
****那建議增至6000bytes再提優良--[[User:Doitagainagain|Doitagainagain]]([[User talk:Doitagainagain|留言]]) 2019年5月31日 (五) 09:44 (UTC)
*****哈哈哈,优良还行。已阅。--<span style="font: 14px Comic Sans MS;text-shadow:0 1px 5px #66f9cf;">[[U:Rowingbohe|Rowing]][[UT:Rowingbohe|bohe♬~]]</span><small>欢迎关注近期[[WP:GAC|优良条目评选]] [[WP:TZ|Taichow]]/[[U:Rowingbohe/访客签名|Sign]]</small> 2019年5月31日 (五) 09:47 (UTC)
******還有,DYK也沒要求內容正確,那麼代表可以亂寫..--[[User:Doitagainagain|Doitagainagain]]([[User talk:Doitagainagain|留言]]) 2019年5月31日 (五) 09:49 (UTC)
*******我用自己的沙盒查過,不計算Infobox和參考來源,正文只有1,626位元組,要是以前烏拉跨氪、街燈這些老前輩大抵會走出來反對。不過約定俗成的規則和正式規則並不是一回事,規則並未規定正文字數必須省略Infobox和參考來源,而且大家看不出問題,只能說大家的尺度寬鬆了。因此駁回。--[[U:春卷柯南|<font color="orange">春-{卷}-柯南</font>]]<font color="green">編輯數突破二萬</font> ( [[UT:春卷柯南|論]][[Special:Contribs/春卷柯南|功]]行[[Special:Diff/50396233|賞]]·[[U:春卷柯南/紀功石碑|刻石留名]] ) 2019年5月31日 (五) 09:56 (UTC)
== foo ==
bar [https://duckduckgo.com] [[User:Dwalden test acctcreation3|Dwalden test acctcreation3]] ([[User talk:Dwalden test acctcreation3|talk]]) 13:05, 14 November 2025 (UTC)
== foo ==
bar [[User:Dwalden test acctcreation3|Dwalden test acctcreation3]] ([[User talk:Dwalden test acctcreation3|talk]]) 13:07, 14 November 2025 (UTC)
1gkn7lcfv00b2ky97wdgn4r7wtzjg7t
Wikipedia:Sandbox
4
107092
739020
737782
2026-04-21T14:56:49Z
Axwvwxz
73360
My first bot edit for learning purposes
739020
wikitext
text/x-wiki
<noinclude>{{Sandbox}}</noinclude>
== Please start your testing below this line ==
Hello from Termux! This edit was made by Axwvwxz using Pywikibot.
s9b8899vk6qi5wb77hr835lp49om52a
739042
739020
2026-04-21T21:00:55Z
Cewbot
33876
Clear the sandbox. If you want to keep it longer, please test it in [[Special:MyPage/Sandbox|personal sandbox]], you can also check the revision history of the sandbox.
739042
wikitext
text/x-wiki
<noinclude>{{Sandbox}}</noinclude>
== Please start your testing below this line ==
9v37rcaxoiwjar8n3q9n7dcsjdvcyin
User:Nardog/sandbox2.js
2
118608
739068
738516
2026-04-22T03:41:26Z
Nardog
40946
739068
javascript
text/javascript
(async function listTools() {
let pageAction = mw.config.get('wgAction');
let isView = pageAction === 'view';
let isEdit = ['edit', 'submit'].includes(pageAction);
if (!isView && !isEdit) return;
let pageType = mw.config.get('wgCanonicalSpecialPageName') ||
mw.config.get('wgNamespaceNumber');
if (isView && !pageType && !mw.config.exists('wgRedirectedFrom') &&
!mw.config.get('wgIsRedirect') &&
!mw.config.get('wgPageName').includes('/')
) {
return;
}
await mw.loader.using([
'mediawiki.util', 'mediawiki.Title', 'mediawiki.api',
'mediawiki.interface.helpers.styles'
]);
mw.loader.addStyleTag(`.listtools:not(#mw-content-subtitle .listtools) {
font-size: 85%;
}
.listtools, .listtools a {
font-weight: normal !important;
font-style: normal;
}
.mw-datatable .listtools {
display: block;
}
.listtools + .mw-whatlinkshere-tools,
#watchlist-edit-form .listtools ~ .mw-changeslist-links,
.mw-special-DisambiguationPageLinks .listtools + a {
display: none;
}`);
let messages = Object.assign({
watched: 'Added "$1" to your watchlist',
watchFail: `Couldn't watch "$1"`,
unwatchFail: `Couldn't unwatch "$1"`
}, window.listtoolsMessages);
let getMsg = (key, ...args) => (
Object.hasOwn(messages, key) ? mw.format(messages[key], ...args) : key
);
let notif;
let watchHandler = async function (e) {
e.preventDefault();
let $link = $(this);
let $wrapper = $link.parent();
$link.detach();
let params = new URLSearchParams(this.search);
let action = params.get('action');
$wrapper.text(getMsg(action + 'ing'));
let pn = params.get('title').replaceAll('_', ' ');
let promise = new mw.Api()[action](pn);
if (notif) {
notif.close();
notif = null;
}
try {
let result = await promise;
if (!result || !result[action + 'ed']) throw '';
let newAction = action === 'watch' ? 'unwatch' : 'watch';
params.set('action', newAction);
$link.add(`.listtools-watch > a[href="${this.pathname + this.search}"]`)
.attr('href', this.pathname + '?' + params)
.text(getMsg(newAction));
if (action !== 'watch') return;
let require = await mw.loader.using([
'mediawiki.notification', 'mediawiki.watchstar.widgets'
]);
notif = await mw.notify(
new (require('mediawiki.watchstar.widgets'))('watch', pn, null, $.noop, {
message: getMsg('watched', pn)
}).$element,
{ tag: 'listtools' }
);
} catch {
notif = await mw.notify(getMsg(action + 'Fail', pn), {
tag: 'listtools',
type: 'error'
});
} finally {
$wrapper.html($link);
}
};
let extGetMain = function () {
return this.title;
};
let re = new RegExp(`(?:\\?title=|${
mw.util.escapeRegExp(mw.format(mw.config.get('wgArticlePath'), ''))
})([^#&?]+)`);
let processed = new WeakSet();
let processLinks = ($links, module, titles) => {
let isBatch = !!titles;
titles = titles || new Set();
$links.each(function (i) {
if (processed.has(this)) return;
let $link = $links.eq(i);
let pn;
if (module.useText) {
pn = $link.text();
} else {
let match = $link.attr('href')?.match(re);
if (!match) return;
pn = decodeURIComponent(match[1]);
}
let t = mw.Title.newFromText(pn);
if (!t) return;
if (module.titlesOnly) {
let text = $link.text();
if (text !== pn.replaceAll('_', ' ') &&
(text !== t.getMainText() || t.namespace === 2)
) {
return;
}
}
if ($link.is('.external, .extiw')) {
Object.assign(t, {
getMain: extGetMain,
host: this.host,
namespace: 0,
title: pn
});
} else {
if (t.namespace < 0) return;
if ($link.hasClass('new')) {
t.missing = true;
}
titles.add(t.getSubjectPage().toText());
}
let $tools = $('<span>').addClass('listtools mw-changeslist-links')
.data('listtools', t);
tools.forEach(tool => {
addTool($tools, tool);
});
if ($link.is(':is(del, bdi) > :only-child')) {
if (module.position === 'end') {
$link.parent().parent().append(' ', $tools);
} else {
$link.parent().after(' ', $tools);
}
} else if (module.position === 'end') {
$link.parent().append(' ', $tools);
} else {
$link.after(' ', $tools);
}
if (module.post) {
module.post($tools);
}
processed.add(this);
});
if (!isBatch) {
getWatched(titles);
}
};
let tools = [
{
name: 'edit',
url: t => t.getUrl({ action: 'edit' })
},
{
name: 'hist',
url: t => !t.missing && t.getUrl({ action: 'history' })
},
{
name: 'links',
url: t => mw.util.getUrl('Special:WhatLinksHere/' + t)
},
{
name: 'watch',
url: t => !t.host && t.getSubjectPage().getUrl({ action: 'watch' }),
callback: watchHandler
}
];
let addTool = ($tools, tool, escapedName) => {
let t = $tools.data('listtools');
let $duplicate = escapedName &&
$tools.children('.listtools-' + escapedName);
let url = tool.url;
if (typeof url === 'function') {
url = url(t);
if (!url) {
$duplicate?.remove();
return;
}
}
let $link = $('<a>').attr('href', url).text(getMsg(tool.name));
if (t.host) {
$link.prop('host', t.host);
}
if (tool.callback) {
$link.on('click', tool.callback);
}
let $wrapper = $('<span>').addClass('listtools-' + tool.name)
.append($link);
let $next = tool.next && $tools.children('.listtools-' + tool.next);
if ($next?.length) {
$duplicate?.remove();
$next.before($wrapper);
} else if ($duplicate?.length) {
$duplicate.replaceWith($wrapper);
} else {
$tools.append($wrapper);
}
};
let extend = tool => {
if (tool.label && !Object.hasOwn(messages, tool.label)) {
messages[tool.name] = tool.label;
}
if (tool.next) {
tool.next = $.escapeSelector(tool.next);
}
let existingTool = tools.find(t => t.name === tool.name);
if (existingTool) {
Object.assign(existingTool, tool);
} else {
tools.push(tool);
}
let escapedName = existingTool && $.escapeSelector(tool.name);
let $allTools = $('.listtools');
$allTools.each(function (i) {
addTool($allTools.eq(i), tool, escapedName);
});
};
let getWatched = async titles => {
if (!Array.isArray(titles)) {
titles = [...titles].slice(0, 500);
}
if (!titles.length) return;
(await new mw.Api().post({
action: 'query',
titles: titles.slice(0, 50),
prop: 'info',
inprop: 'watched',
formatversion: 2
}, {
headers: { 'Promise-Non-Write-API-Action': 1 }
})).query.pages.forEach(page => {
if (!page.watched) return;
$(`.listtools-watch > a[href="${mw.util.getUrl(page.title, { action: 'watch' })}"]`)
.attr('href', mw.util.getUrl(page.title, { action: 'unwatch' }))
.text(getMsg('unwatch'));
});
getWatched(titles.slice(50));
};
mw.hook('listtools.ready').fire(extend);
let catTreeCallback = (records, observer) => {
let $links = $(records[0].target).find('.CategoryTreeItem > bdi > a');
if ($links.length) {
observer.takeRecords();
observer.disconnect();
processLinks($links, catTreeModule);
}
};
let catTreeModule = {
selector: '.CategoryTreeItem > bdi > a',
types: [14, 'CategoryTree'],
position: 'end',
post: $tools => {
$tools.parent().next('.CategoryTreeChildren').each(function () {
new MutationObserver(catTreeCallback)
.observe(this, { childList: true });
});
}
};
let modules = [
{
selector: '#mw-pages li > a, #mw-pages li > span > a',
types: [14]
},
catTreeModule,
{
selector: '#mw-imagepage-section-linkstoimage a, #mw-imagepage-section-globalusage a',
types: [6]
},
{
selector: '#mw-globalusage-result a',
types: ['GlobalUsage']
},
{
selector: '.mw-search-result-heading > a, .searchalttitle > a.mw-redirect, .iw-result__title > a, .mw-search-exists a',
types: ['Search']
},
{
selector: '.mw-search-createlink a',
types: ['Search'],
titlesOnly: true
},
{
selector: '#watchlist-edit-form .cdx-table td > label > a',
types: ['EditWatchlist']
},
{
selector: '.plainlinks > li > a',
types: ['AbuseLog'],
titlesOnly: true
},
{
selector: '#mw-allmessagestable td:first-child > a:first-child:not(.new)',
types: ['Allmessages'],
position: 'end'
},
{
selector: '.mw-spcontent li a',
types: ['DisambiguationPageLinks', 'Listredirects'],
titlesOnly: true
},
{
selector: 'li > a:first-child',
types: ['FileDuplicateSearch']
},
{
selector: '.TablePager_col_title > a:first-child, .TablePager_col_template > a',
types: ['LintErrors'],
post: $tools => {
$tools.parent().contents().slice(3).remove();
}
},
{
selector: 'form > ul > li > a',
types: ['Nuke'],
position: 'end',
titlesOnly: true
},
{
selector: '.page-assessments a',
types: ['PageAssessments'],
titlesOnly: true
},
{
selector: '.TablePager_col_pr_page > a',
types: ['Protectedpages'],
position: 'end'
},
{
selector: '#mw-content-text > ul a',
types: ['Protectedtitles'],
position: 'end'
},
{
selector: '.mw-fr-pending-changes-page-title',
types: ['PendingChanges'],
post: $tools => {
$tools.parent().contents().slice(3).remove();
}
},
{
selector: '#mw-content-text > ul a:first-child',
types: ['StablePages'],
position: 'end'
},
{
selector: '.TablePager_col__page a',
types: ['TopicSubscriptions']
},
{
selector: '.undeleteResult > a',
types: ['Undelete'],
position: 'end',
useText: true
},
{
selector: '.TablePager_col_img_name > a:first-child',
// types: ['Listfiles'],
position: 'end'
},
{
selector: '.mw-newpages-pagename',
post: $tools => {
let $nodes = $tools.parent().contents();
$nodes.slice(
$nodes.index($tools) + 1,
$nodes.index($nodes.filter('.mw-newpages-length'))
).replaceWith(' ');
}
},
{
selector: '#mw-whatlinkshere-list li > bdi > a'
},
{
selector: '.mw-changeslist-log-entry > a:not(.mw-changeslist-log-gblblock a, .mw-changeslist-log-globalauth a)',
titlesOnly: true
},
{
selector: '.mw-logevent-loglines > li:not(.mw-logline-gblblock, .mw-logline-globalauth) > a',
types: ['Log'],
titlesOnly: true
},
{
selector: '#mw-diff-otitle1 > strong > a, #mw-diff-ntitle1 > strong > a',
types: ['ComparePages'],
position: 'end'
},
{
selector: '#movepage-oldlink, #movepage-newlink',
types: ['Movepage']
},
{
selector: '.mw-undelete-revision a:not(.mw-userlink, .mw-usertoollinks > a)',
types: ['Undelete'],
useText: true
},
{
selector: '.galleryfilename, ' +
'.mw-allpages-chunk > li > a, ' +
'.mw-prefixindex-list > li > a, ' +
'.mw-changeslist-line.mw-changeslist-src-mw-categorize .mw-changeslist-line-inner > .comment > a, ' +
'.mw-changeslist-line.mw-changeslist-src-mw-categorize .mw-changeslist-line-inner-comment > .comment > a, ' +
'.mw-changeslist-line.mw-changeslist-src-mw-categorize .mw-enhanced-rc-nested > .comment > a'
},
{
selector: '.mw-spcontent li a',
position: 'end',
titlesOnly: true
}
];
if (isEdit) {
let post = $tools => {
if (!$tools[0].closest('.templatesUsed')) return;
$tools.parent().contents().last().each(function () {
this.textContent = this.textContent.slice(1);
}).end().slice(-3, -1).remove();
};
let callback = mw.util.debounce(() => {
processLinks(
$('.mw-editfooter-list a, #wikiPreview > .previewnote a'),
{ titlesOnly: true, post }
);
}, 500);
mw.hook('wikipage.editform').add($form => {
callback();
$form.find('.templatesUsed').each(function () {
if (processed.has(this)) return;
processed.add(this);
new MutationObserver(callback)
.observe(this, { childList: true, subtree: true });
});
});
} else if (typeof pageType === 'number') {
$(() => {
processLinks($('.subpages a, .mw-redirectedfrom a, .redirectText a'), {});
});
}
mw.hook('wikipage.content').add($content => {
let titles = new Set();
let $links = $content.find('a');
modules.forEach(module => {
if (module.types && !module.types.includes(pageType)) return;
processLinks($links.filter(module.selector), module, titles);
});
getWatched(titles);
});
}());
mw.hook('listtools.ready').add(extend => {
// extend({
// name: 'talk',
// url: t => !t.isTalkPage() && t.canHaveTalkPage() && t.getTalkPage().getUrl(),
// next: 'hist'
// });
extend({
name: 'subject',
url: t => t.isTalkPage() && t.getSubjectPage().getUrl(),
next: 'hist'
});
extend({
name: 'last',
url: t => !t.missing && t.getUrl({ diff: 'cur', diffonly: 1 }),
next: 'links'
});
// extend({
// name: 'purge',
// url: t => t.getUrl({ action: 'purge' }),
// next: 'watch',
// callback: function (e) {
// e.preventDefault();
// let $link = $(this);
// let $wrapper = $link.parent();
// $link.detach();
// $wrapper.text('purging');
// let pn = $wrapper.closest('.listtools').data('listtools').toText();
// new mw.Api().post({
// action: 'purge',
// forcelinkupdate: 1,
// titles: pn,
// formatversion: 2
// }).then(response => {
// if (response.purge[0].purged) {
// mw.notify(`Purged "${pn}"'`);
// }
// }).always(() => {
// $wrapper.html($link);
// });
// }
// });
extend({
name: 'copy',
url: '#',
callback: function (e) {
e.preventDefault();
let text = $(this).closest('.listtools').data('listtools').toText();
let $input = $('<input>').attr({
type: 'text',
readonly: '',
style: 'position:fixed;top:-100%'
}).val(text).appendTo(document.body);
$input[0].select();
let copied;
try {
copied = document.execCommand('copy');
} catch (err) {}
$input.remove();
if (copied) {
mw.notify(`Copied "${text}"`);
} else {
mw.notify('Copy failed', { type: 'error' });
}
}
});
});
(mw.config.get('wgNamespaceNumber') || mw.config.get('wgAction') !== 'view') &&
mw.config.get('wgCanonicalSpecialPageName') !== 'GlobalContributions' &&
(function consecudiff() {
mw.loader.addStyleTag('.consecudiff::before{content:" ["} .consecudiff::after{content:"]"} .consecudiff-top::before{content:" ⟨"} .consecudiff-top::after{content:"⟩"}');
let isHist = mw.config.get('wgAction') === 'history';
class Consecudiff {
constructor(lis, isContribs) {
this.isContribs = isContribs;
this.isEnhanced = !isHist && !isContribs &&
lis[0].classList.contains('mw-enhanced-rc');
this.threshold = isContribs ? window.consecudiffContribsThreshold || 120
: isHist ? window.consecudiffHistThreshold || 720
: window.consecudiffThreshold || 720;
this.strictMode = !isContribs &&
!!window.consecudiffDetectInterruptions;
this.diffSelector = isHist
? 'a.mw-history-histlinks-previous'
: '.mw-changeslist-diff';
this.permaSelector = this.isEnhanced && '.mw-enhanced-rc-time > a' ||
(isHist || isContribs) && 'a.mw-changeslist-date';
this.hybridSelector = this.diffSelector;
if (this.permaSelector) {
this.hybridSelector += ', ' + this.permaSelector;
}
this.topClass = isContribs
? 'mw-contributions-current'
: 'mw-changeslist-last';
let dependencies = ['mediawiki.util'];
if ((isHist || isContribs) && mw.config.get('wgUserLanguage') !== 'en') {
dependencies.push('mediawiki.language.months');
}
mw.loader.using(dependencies, () => {
let chunks;
if (isHist) {
chunks = this.chunkByUser(lis);
} else {
chunks = [];
this.groupByTitle(lis).forEach(group => {
chunks.push(...this.chunkByUser(group));
});
}
let subchunks = [];
chunks.forEach(chunk => {
subchunks.push(...this.divideByDate(chunk));
});
let linkPairs = [];
subchunks.forEach(subchunk => {
linkPairs.push(...this.makeLinks(subchunk));
});
linkPairs.forEach(([$span, parent]) => {
$span.appendTo(parent);
});
});
}
groupByTitle(lis) {
let selector = this.isContribs
? '.mw-contributions-title'
: '.mw-changeslist-title';
let lisByTitle = {};
lis.forEach(li => {
let link = (this.isEnhanced ? li.closest('table') : li)
.querySelector(selector);
if (!link) return;
let title = link.textContent;
if (!lisByTitle.hasOwnProperty(title)) {
lisByTitle[title] = [];
}
lisByTitle[title].push(li);
});
return Object.values(lisByTitle).filter(group => group.length > 1);
}
chunkByUser(lis) {
if (this.isSingleContribs) {
return [lis];
}
let chunks = [], lastSplitAt = 0, prevUser;
this.isSingleContribs = lis.some((li, i) => {
let link = li.querySelector('.mw-userlink');
if (!link && this.isContribs) {
return true;
}
let user = link && link.textContent;
if (!link || i && user !== prevUser) {
chunks.push(lis.slice(lastSplitAt, i));
lastSplitAt = i;
}
prevUser = user;
});
if (this.isSingleContribs) {
return [lis];
}
chunks.push(lis.slice(lastSplitAt));
return chunks.filter(chunk => chunk.length > 1);
}
divideByDate(lis) {
let chunks = [], lastSplitAt = 0, prevDate;
lis.forEach((li, i) => {
let date;
if (isHist || this.isContribs) {
date = this.parseDate(
li.querySelector('.mw-changeslist-date').textContent
);
} else {
date = Date.parse(
li.dataset.mwTs.replace(/(....)(..)(..)(..)(..)(..)/, '$1-$2-$3T$4:$5:$6Z')
);
}
if (date) {
date = date / 60000;
}
if (i && prevDate - date > this.threshold) {
chunks.push(lis.slice(lastSplitAt, i));
lastSplitAt = i;
}
prevDate = date;
if (!this.strictMode || lastSplitAt === i) return;
let prevDiff = lis[i - 1].querySelector(this.diffSelector);
if (prevDiff) {
let prevNext = mw.util.getParamValue('oldid', prevDiff.search);
if (prevNext !== li.dataset.mwRevid) {
chunks.push(lis.slice(lastSplitAt, i));
lastSplitAt = i;
}
}
});
chunks.push(lis.slice(lastSplitAt));
return chunks.filter(chunk => chunk.length > 1);
}
makeLinks(lis) {
let count = lis.length;
let firstPerma;
let start = lis.findIndex(li => (
firstPerma = li.querySelector(this.hybridSelector)
));
if (start === -1 || count - start < 2) return [];
let end, lastDiff;
for (let i = count - 1; i > start; i--) {
if (!isHist && !this.isContribs) {
lastDiff = lis[i].querySelector(this.diffSelector);
if (lastDiff ||
lis[i].classList.contains('mw-changeslist-src-mw-new')
) {
end = i + 1;
break;
}
}
if (this.permaSelector && lis[i].querySelector(this.permaSelector)) {
end = i + 1;
break;
}
}
if (!end) return [];
count = end - start;
let params = { diff: lis[start].dataset.mwRevid };
if (lastDiff) {
params.oldid = mw.util.getParamValue('oldid', lastDiff.search);
} else {
params.oldid = lis[end - 1].dataset.mwRevid;
if (isHist && lis[end - 1].querySelector(this.diffSelector) ||
this.isContribs && !lis[end - 1].querySelector('.newpage')
) {
params.direction = 'prev';
}
}
let title = !isHist && mw.util.getParamValue('title', firstPerma.search);
let url = mw.util.getUrl(title, params);
let classes = 'consecudiff';
if (!isHist && lis[start].classList.contains(this.topClass)) {
classes += ' consecudiff-top';
}
return lis.slice(start, end).map((li, i) => [
$('<span>').addClass(classes).append(
$('<a>')
.attr('href', url)
.text(this.convertNumber(count - i + '/' + count))
),
this.isEnhanced
? li.tagName === 'TR'
? li.lastElementChild
: li.querySelector('.mw-changeslist-line-inner')
: li
]);
}
parseDate(s) {
let date = Date.parse(s);
if (date) {
return date;
}
if (s.includes(',')) date = Date.parse(s.replace(',', ''));
if (date) {
return date;
}
if (mw.loader.getState('mediawiki.language.months') !== 'ready') return;
s = s.replace(/\D/g, c => {
let n = mw.language.convertNumber(c, true);
return Number.isNaN(n) ? c : n;
});
let h, m;
s = s.replace(/(\d\d?)[.:h](\d\d?)/, ($0, $1, $2) => {
h = $1;
m = $2;
return ' ';
});
if (!h) return;
let y, dateFirst;
s = s.replace(/^(.*?)(\d{4})(?!\d)/, ($0, $1, $2) => {
y = $2;
dateFirst = /\d/.test($1);
return $1 + ' ';
});
if (!y) return;
let mo, d;
if (dateFirst) {
[d, s] = this.getDate(s);
if (!d) return;
[mo, s] = this.getMonth(s);
if (mo === -1) return;
} else {
[mo, s] = this.getMonth(s);
if (mo === -1) return;
[d, s] = this.getDate(s);
if (!d) return;
}
return new Date(y, mo, d, h, m).getTime();
}
getMonth(s) {
if (!this.months) {
this.months = mw.language.months.abbrev
.concat(mw.language.months.names, mw.language.months.genitive)
.reverse();
}
let mo = this.months.findIndex(mn => {
let temp = s.replace(mn, ' ');
if (temp !== s) {
s = temp;
return true;
}
});
if (mo === -1) {
let [numeric, temp] = this.getDate(s);
numeric = parseInt(numeric);
if (numeric > 0 && numeric < 13) {
mo = numeric - 1;
s = temp;
}
} else {
mo = 11 - mo % 12;
}
return [mo, s];
}
getDate(s) {
let d;
s = s.replace(/(^|\D)(\d\d?)(?!\d)/, ($0, $1, $2) => {
d = $2;
return $1 + ' ';
});
return [d, s];
}
convertNumber(num) {
try {
return mw.language.convertNumber(num);
} catch (e) {
return num;
}
}
}
mw.hook('wikipage.content').add($content => {
$content.find('.mw-pager-body').each(function () {
let lis = this.querySelectorAll('.mw-contributions-list > li');
if (lis.length > 1) {
new Consecudiff([...lis], !isHist);
}
});
if (isHist) return;
let $lists = $content.filter('.mw-changeslist');
if (!$lists.length) {
$lists = $content.find('.mw-changeslist');
}
$lists.each(function () {
let lis = this.querySelectorAll('.mw-changeslist-edit:not(.mw-changeslist-src-mw-categorize)[data-mw-revid]');
if (lis.length > 1) {
new Consecudiff([...lis]);
}
});
});
}());
if (mw.config.get('wgNamespaceNumber') === 14 && (
mw.config.get('wgAction') === 'view' || !mw.config.get('wgArticleId')
)) {
mw.loader.load('//test.wikipedia.org/w/index.php?title=User:Nardog/sandbox8.js&action=raw&ctype=text/javascript');
mw.loader.using([
'mediawiki.api', 'mediawiki.util', 'mediawiki.DateFormatter',
'oojs-ui-widgets', 'mediawiki.widgets',
'mediawiki.widgets.UserInputWidget', 'mediawiki.widgets.datetime',
'oojs-ui.styles.icons-interactions', 'oojs-ui.styles.icons-movement',
'mediawiki.interface.helpers.styles', 'user.options'
]);
}
$(function moveHistory() {
if (!document.getElementById('p-tb')) return;
mw.loader.using('mediawiki.util', () => {
let clicked;
mw.util.addPortletLink('p-tb', '#', 'Move history', 't-movehistory').firstElementChild.addEventListener('click', e => {
e.preventDefault();
if (clicked) {
if (window.moveHistoryDialog) {
window.moveHistoryDialog.open();
}
return;
}
clicked = true;
mw.loader.load('//test.wikipedia.org/w/index.php?title=User:Nardog/sandbox5.js&action=raw&ctype=text/javascript');
mw.loader.using([
'mediawiki.api', 'mediawiki.util', 'mediawiki.Title', 'mediawiki.DateFormatter',
'oojs-ui-windows', 'oojs-ui-widgets', 'mediawiki.widgets',
'mediawiki.widgets.DateInputWidget', 'oojs-ui.styles.icons-interactions',
'mediawiki.interface.helpers.styles'
]);
});
});
});
$(function sectionSearch() {
if (!document.getElementById('p-tb')) return;
mw.loader.using('mediawiki.util', () => {
let clicked;
mw.util.addPortletLink('p-tb', '#', 'Section search', 't-sectionsearch').firstElementChild.addEventListener('click', e => {
e.preventDefault();
if (clicked) {
if (window.sectionSearchDialog) {
window.sectionSearchDialog.open();
}
return;
}
clicked = true;
mw.loader.load('//test.wikipedia.org/w/index.php?title=User:Nardog/sandbox7.js&action=raw&ctype=text/javascript');
mw.loader.using([
'mediawiki.api', 'mediawiki.util', 'oojs-ui-core', 'oojs-ui-windows',
'mediawiki.widgets', 'mediawiki.widgets.NamespacesMultiselectWidget'
]);
});
});
});
mw.config.get('wgCanonicalSpecialPageName') === 'CentralAuth' &&
mw.loader.using('jquery.tablesorter', function sortCentralAuthByEditCount() {
mw.hook('wikipage.content').add($content => {
let $table = $content.find('.mw-centralauth-wikislist').has('td');
if (!$table.length) return;
$table.tablesorter().data('tablesorter').sort([{ 4: 'desc' }, { 1: 'asc' }]);
});
});
['edit', 'submit'].includes(mw.config.get('wgAction')) &&
[10, 828].includes(mw.config.get('wgNamespaceNumber')) &&
!mw.config.get('wgTitle').endsWith('/doc') &&
mw.loader.load('//en.wikipedia.org/w/index.php?title=User:Nardog/AutoTestcases.js&action=raw&ctype=text/javascript', 's');
// ['edit', 'submit'].includes(mw.config.get('wgAction')) &&
// mw.loader.load('//en.wikipedia.org/w/index.php?title=User:Nardog/TemplatePreviewGuard.js&action=raw&ctype=text/javascript', 's');
// ['edit', 'submit'].includes(mw.config.get('wgAction')) &&
// $(function templatePreviewGuard() {
// let button = document.querySelector('input[name="wpTemplateSandboxPreview"]');
// if (!button) return;
// let proceed;
// button.addEventListener('click', e => {
// if (proceed) {
// proceed = false;
// return;
// }
// e.preventDefault();
// e.stopPropagation();
// let formData = new FormData(button.form);
// let page = formData.get('wpTemplateSandboxPage');
// let temp = formData.get('wpTemplateSandboxTemplate');
// if (!page || !temp) return;
// mw.loader.using('mediawiki.api').then(() => (
// new mw.Api().get({
// action: 'query',
// titles: page,
// prop: 'templates',
// tltemplates: temp,
// formatversion: 2
// })
// )).always(response => {
// if (((((response || {}).query || {}).pages || [])[0] || {}).templates ||
// confirm(`"${page}" doesn't appear to transclude "${temp}". Continue?`)
// ) {
// proceed = true;
// button.click();
// }
// });
// }, true);
// if (!mw.config.get('wgArticleId')) return;
// let widgetEl = document.querySelector('#wpTemplateSandboxPage.oo-ui-widget');
// if (!widgetEl) return;
// let pn = mw.config.get('wgPageName').replace(/_/g, ' ');
// mw.loader.using(['mediawiki.api', 'oojs-ui-core']).then(() => (
// new mw.Api().get({
// action: 'query',
// titles: pn,
// prop: 'transcludedin',
// tiprop: 'title',
// tilimit: 'max',
// formatversion: 2
// })
// )).then(response => {
// if (!response.batchcomplete) return;
// let pages = response.query.pages[0].transcludedin
// .filter(o => o.title !== pn);
// if (!pages.length) return;
// let widget = OO.ui.infuse(widgetEl);
// if (pages.length === 1) {
// widget.setValue(pages[0].title);
// return;
// }
// widget.$element.replaceWith(
// new OO.ui.ComboBoxInputWidget({
// id: 'wpTemplateSandboxPage',
// maxlength: widget.$input.prop('maxLength'),
// name: widget.$input.prop('name'),
// options: pages
// .sort((a, b) => a.ns - b.ns || -(a.title < b.title))
// .map(o => ({ data: o.title })),
// placeholder: widget.$input.prop('placeholder'),
// tabIndex: widget.getTabIndex(),
// value: widget.getValue()
// }).on('enter', e => {
// e.preventDefault();
// button.click();
// }).$element
// );
// });
// });
['edit', 'submit'].includes(mw.config.get('wgAction')) &&
mw.config.get('wgArticleId') &&
mw.config.get('wgPageContentModel') === 'wikitext' &&
$(async () => {
let form = document.getElementById('editform');
if (!form) return;
let formData = new FormData(form);
let section = formData.get('wpSection');
if (section === 'new') return;
let widget = document.getElementById('wpSummaryWidget');
if (!widget) return;
let isOld = formData.get('altBaseRevId') > 0 ||
(formData.get('baseRevId') || formData.get('parentRevId')) !== formData.get('editRevId');
await mw.loader.using([
'jquery.textSelection', 'mediawiki.util', 'mediawiki.api', 'oojs-ui-core',
'oojs-ui.styles.icons-editing-core'
]);
let $textarea = $('#wpTextbox1');
let input = OO.ui.infuse(widget);
let button = new OO.ui.ButtonWidget({
framed: false,
icon: 'undo',
classes: ['autosectionlink-button'],
invisibleLabel: true,
label: 'Restore previous section link'
}).toggle().on('click', () => {
let cache = button.getData();
input.setValue(input.getValue().replace(
/^(\/\*.*?\*\/)?\s*/,
cache[0] ? '/* ' + cache[0] + ' */ ' : ''
));
updatePreview(cache[0]);
cache.reverse();
}).on('toggle', () => {
input.$input.css('width', `calc(100% - ${button.$element.width()}px)`);
});
input.$input.after(button.$element);
let update = mw.util.debounce($diff => {
let lines = $textarea.textSelection('getContents').trimEnd().split('\n');
let firstLineNum;
if (isOld) {
let i, lastLineNum;
$diff.find('td:last-child').each(function () {
if (this.classList.contains('diff-lineno')) {
i = this.textContent.replace(/\D+/g, '') - 1;
} else if (this.classList.contains('diff-context')) {
i++;
} else if (this.classList.contains('diff-addedline')) {
i++;
if (!firstLineNum) {
firstLineNum = i;
}
lastLineNum = i;
} else if (this.classList.contains('diff-empty')) {
if (!firstLineNum) {
firstLineNum = i === 0 ? 1 : i;
}
lastLineNum = i;
}
});
lines.length = lastLineNum || 0;
} else {
let origLines = $textarea.prop('defaultValue').trimEnd().split('\n');
firstLineNum = lines.findIndex((line, i) => line !== origLines[i]) + 1;
if (!firstLineNum) {
firstLineNum = lines.length < origLines.length
? lines.length
: 1;
}
for (let i = 1, x = lines.length, y = origLines.length;
(section ? i < x : i <= x) && lines[x - i] === origLines[y - i];
i++
) {
lines.pop();
}
}
let re = /^(={1,6})\s*(.+?)\s*\1\s*(?:<!--.+-->\s*)?$/, lowest = 7;
lines.slice(firstLineNum).forEach(line => {
let match = line.match(re);
if (match?.[1].length < lowest) {
lowest = match[1].length;
}
});
let head;
lines.slice(0, firstLineNum).reverse().some(line => {
let match = line.match(re);
if (match?.[1].length < lowest) {
head = match[2];
return true;
}
});
if (head) {
head = head
.replace(/'''(.+?)'''|\[\[:?(?:[^|\]]+\|)?([^\]]+)\]\]|<\/?(?:abbr|b|bdi|bdo|big|cite|code|data|del|dfn|em|font|i|ins|kbd|mark|nowiki|q|rb|ref|rp|rt|rtc|ruby|s|samp|small|span|strike|strong|sub|sup|templatestyles|time|translate|tt|u|var)(?:\s[^>]*)?>|<!--.*?-->|\[(?:https?:)?\/\/[^\s\[\]]+\s([^\]]+)\]/gi, '$1$2$3')
.replace(/''(.+?)''/g, '$1')
.trim();
} else if (lines.length && section < 1 && lowest === 7) {
head = '';
}
let v = input.getValue();
let match = v.match(/^\/\*\s*(.+?)\s*\*\/\s*/);
let prev = match?.[1];
if (prev === head) return;
input.setValue((
head || head === '' ? '/* ' + head + ' */ ' : ''
) + (match ? v.slice(match[0].length) : v));
button.setData([prev, head]).toggle(true);
updatePreview(head);
}, 500);
let updatePreview = head => {
let $comment = $('.mw-summary-preview > .comment');
if (!$comment.length) return;
let url;
if (head) {
url = mw.util.getUrl() + '#' + head.replace(/[\s_]+/g, '_');
} else if (head === '') {
head = mw.messages.get('autocomment-top', '(top)');
url = mw.util.getUrl();
}
let paren = [...mw.messages.get('parentheses', '($1)')][0];
let $nodes = $comment.contents();
let $ac = $nodes.eq(1);
if ($nodes[0]?.textContent === paren && $ac.is('.autocomment:first-child')) {
if (head) {
$ac.children('a').attr('href', url).children('bdi').text(head);
} else {
if ($nodes[2]?.nodeType === 3) {
$nodes[2].textContent = $nodes[2].textContent.trimStart();
}
$ac.remove();
}
} else if (head) {
let rtl = document.body.classList.contains('sitedir-rtl');
$comment.prepend(
paren,
$('<span>').addClass('autocomment').append(
$('<a>').attr({
href: url,
title: mw.config.get('wgPageName').replaceAll('_', ' ')
}).text(rtl ? '←' : '→').append(
$('<bdi>').attr('dir', rtl ? 'rtl' : 'ltr').text(head)
),
mw.messages.get('colon-separator', ': ')
)
);
if ($nodes[0]?.nodeType === 3) {
let text = $nodes[0].textContent;
if (text.startsWith(paren)) {
text = text.slice(paren.length);
}
$nodes[0].textContent = ' ' + text;
}
}
};
if (isOld) {
mw.hook('wikipage.diff').add(update);
} else {
$textarea.on('input', update);
mw.hook('ext.CodeMirror.input').add(update);
update();
}
new mw.Api().loadMessagesIfMissing(['autocomment-top', 'colon-separator', 'parentheses']);
mw.loader.addStyleTag('.autosectionlink-button{position:absolute;top:0;right:0;margin:0}');
});
(mw.config.get('wgNamespaceNumber') === -1 || mw.config.exists('wgDiffNewId') ||
mw.config.get('wgAction') === 'history') &&
(function copyRevId() {
let handler = function (e) {
e.preventDefault();
let text = this.closest('.diff td')?.querySelector('[data-mw-revid]')?.dataset.mwRevid ||
this.closest('[data-mw-revid]')?.dataset.mwRevid;
if (!text) return;
let $input = $('<input>').attr({
type: 'text',
readonly: '',
style: 'position:fixed;top:-100%'
}).val(text).appendTo(document.body);
$input[0].select();
document.execCommand('copy');
$input.remove();
let copied;
try {
copied = document.execCommand('copy');
} catch {}
$input.remove();
if (copied) {
mw.notify(`Copied "${text}"`);
} else {
mw.notify('Copy failed', { type: 'error' });
}
};
mw.hook('wikipage.diff').add($diff => {
$diff.find('#mw-diff-otitle1, #mw-diff-ntitle1').append(
' (',
$('<a>').attr({
href: '#',
role: 'button'
}).on('click', handler).text('id'),
')'
);
});
if (mw.config.get('wgAction') !== 'history') return;
mw.hook('wikipage.content').add($content => {
$content.find('.mw-pager-tools').append(
$('<span>').append(
$('<a>').attr({
href: '#',
role: 'button'
}).on('click', handler).text('id')
)
);
});
}());
(mw.config.get('wgNamespaceNumber') === -1 || mw.config.exists('wgDiffNewId') ||
mw.config.get('wgAction') === 'history') &&
(() => {
let handler = async function (e) {
e.preventDefault();
let td = this.closest('.diff td');
let rev = td
? td.querySelector('[data-mw-revid]')?.dataset.mwRevid
: this.closest('[data-mw-revid]')?.dataset.mwRevid;
if (!rev) {
mw.notify(`Couldn't get the revision.`, {
tag: 'markasunseen',
type: 'error'
});
return;
}
let pn = td
? new URLSearchParams([...td.querySelectorAll('a')].pop()?.search).get('title')
: mw.config.get('wgPageName');
if (!pn) return;
await mw.loader.using('mediawiki.api');
let result = (await new mw.Api().postWithEditToken({
action: 'setnotificationtimestamp',
[td ? 'newerthanrevid' : 'torevid']: rev,
titles: pn,
formatversion: 2
})).setnotificationtimestamp?.[0];
if (Object.hasOwn(result, 'notificationtimestamp')) {
mw.notify(`Marked revisions ${td ? 'after' : 'since'} ${rev} as unseen.`, {
tag: 'markasunseen',
type: 'success'
});
} else if (result?.notwatched) {
mw.notify('This page is not on your watchlist.', {
tag: 'markasunseen',
type: 'warn'
});
} else {
mw.notify(`Couldn't mark revisions ${td ? 'after' : 'since'} ${rev} as unseen.`, {
tag: 'markasunseen',
type: 'error'
});
}
};
mw.hook('wikipage.diff').add($diff => {
$diff.find('#mw-diff-otitle1').append(
' (',
$('<a>').attr({
href: '#',
role: 'button',
title: 'Mark revisions after this one as unseen'
}).on('click', handler).text('unseen'),
')'
);
});
if (mw.config.get('wgAction') !== 'history') return;
mw.hook('wikipage.content').add($content => {
$content.find('.mw-pager-tools').append(
$('<span>').append(
$('<a>').attr({
href: '#',
role: 'button',
title: 'Mark revisions since this one as unseen'
}).on('click', handler).text('unseen')
)
);
});
})();
(mw.config.get('wgNamespaceNumber') === -1 || mw.config.exists('wgDiffNewId') ||
mw.config.get('wgAction') === 'history') &&
((mw.config.get('wgNamespaceNumber') % 2 || mw.config.get('wgNamespaceNumber') === 4) ||
(mw.config.get('wgWikiID') === 'metawiki' && mw.config.get('wgPageContentModel') === 'wikitext')) &&
mw.loader.using(['mediawiki.util', 'mediawiki.Title'], function copyUnsig() {
let handler = function (e) {
e.preventDefault();
let parent = this.closest('li, td');
let ts = parent.textContent.match(/\d\d:\d\d, \d\d? [A-Z][a-z]+ \d{4}/)?.[0];
if (!ts) return;
let user = parent.querySelector('.mw-userlink').textContent;
if (mw.util.isIPv6Address(user)) {
user = user.toUpperCase();
}
let temp = mw.util.isIPAddress(user) ? 'unsigned IP' : 'unsigned';
let text = `{{subst:${temp}|${user}|${ts}}}`;
let $input = $('<input>').attr({
type: 'text',
readonly: '',
style: 'position:fixed;top:-100%'
}).val(text).appendTo(document.body);
$input[0].select();
let copied;
try {
copied = document.execCommand('copy');
} catch {}
$input.remove();
if (copied) {
mw.notify(`Copied "${text}"`);
} else {
mw.notify('Copy failed', { type: 'error' });
}
};
mw.hook('wikipage.diff').add($diff => {
$diff.find('#mw-diff-otitle1, #mw-diff-ntitle1').filter(function () {
if (mw.config.get('wgWikiID') === 'metawiki') {
return true;
}
let link = this.querySelector('strong > a') ||
this.parentElement.querySelector('#differences-prevlink, #differences-nextlink');
if (!link) return;
let t = mw.Title.newFromText(mw.util.getParamValue('title', link.search));
return t.isTalkPage() || t.namespace === 4;
}).append(
' (',
$('<a>').attr({
href: '#',
role: 'button'
}).on('click', handler).text('sig'),
')'
);
});
if (mw.config.get('wgAction') !== 'history') return;
mw.hook('wikipage.content').add($content => {
$content.find('.mw-pager-tools').append(
$('<span>').append(
$('<a>').attr({
href: '#',
role: 'button'
}).on('click', handler).text('sig')
)
);
});
});
// mw.config.get('wgAction') === 'history' &&
// mw.loader.using('mediawiki.util', function () {
// mw.hook('wikipage.content').add($content => {
// $content.find('a.mw-changeslist-date').after(function () {
// return [
// ' (',
// $('<a>').attr('href', mw.util.getUrl(null, {
// action: 'edit',
// oldid: this.closest('li').dataset.mwRevid
// })).text('e'),
// ')'
// ];
// });
// });
// });
// ['Contributions', 'IPContributions', 'Blankpage'].includes(mw.config.get('wgCanonicalSpecialPageName')) &&
// mw.hook('wikipage.content').add($content => {
// $content.find('.mw-changeslist-history').parent().after(function () {
// return $('<span>').append(
// $('<a>').attr(
// 'href',
// this.firstElementChild.getAttribute('href').slice(0, -7) + 'edit'
// ).text('e')
// );
// });
// });
if (screen.width < 500) {
mw.loader.addStyleTag('@font-face{font-family:CharisW;src:url(//fontlibrary.org/assets/fonts/charis/10b9f94ed21e56254b068c91ead7ec6f/017b2b2ad86e09d3c22b8cf0dfc78247/CharisSILRegular.ttf) format(truetype)} @font-face{font-family:CharisW;font-weight:700;src:url(//fontlibrary.org/assets/fonts/charis/10b9f94ed21e56254b068c91ead7ec6f/6f5069ac6a300dad45383c952e92c573/CharisSILBold.ttf) format(truetype)} body .IPA{font-family:CharisW,sans-serif} .mw-highlight-lines > pre{width:120em}');
location.hash && $(() => {
let target = document.querySelector(':target');
if (target?.getBoundingClientRect().top < 0) {
target.scrollIntoView();
}
});
}
['edit', 'submit'].includes(mw.config.get('wgAction')) &&
(mw.config.exists('wgCodeEditorCurrentLanguage') ||
mw.config.exists('cmMode') && mw.config.get('cmMode') !== 'mediawiki') &&
(function saveNEdit() {
let notif;
$(document.body).on('click', '#wpSave', async function (e) {
if (e.ctrlKey || e.shiftKey || e.metaKey || e.altKey ||
e.originalEvent?.defaultPrevented
) {
return;
}
e.preventDefault();
await mw.loader.using([
'mediawiki.api', 'mediawiki.util', 'jquery.textSelection', 'oojs-ui-core'
]);
let button = OO.ui.infuse(this.parentElement).setDisabled(true);
let $textarea = $('#wpTextbox1');
let text = $textarea.textSelection('getContents');
let $summary = $('#wpSummary');
let formData = new FormData(this.form);
let promise = new mw.Api().postWithEditToken({
action: 'edit',
title: mw.config.get('wgPageName'),
text: text,
section: formData.get('wpSection') || undefined,
summary: $summary.textSelection('getContents'),
[$('#wpMinoredit').prop('checked') ? 'minor' : 'notminor']: 1,
baserevid: formData.get('editRevId'),
basetimestamp: formData.get('wpEdittime'),
starttimestamp: formData.get('wpStarttime'),
watchlist: $('#wpWatchthis').prop('checked') ? 'watch' : 'unwatch',
watchlistexpiry: formData.get('wpWatchlistExpiry') || undefined,
undo: formData.get('wpUndidRevision') || undefined,
undoafter: formData.get('wpUndoAfter') || undefined,
contentformat: formData.get('format'),
contentmodel: formData.get('model'),
assertuser: mw.config.get('wgUserName'),
formatversion: 2
});
notif?.close();
notif = null;
try {
let response = await promise;
if (response?.edit?.result !== 'Success') throw '';
$('#editform > input[name="wpUndidRevision"], #editform > input[name="wpUndoAfter"]').remove();
$textarea.data('origtext', text).prop('defaultValue', text);
$summary.val($summary.prop('defaultValue'));
if (mw.loader.getState('mediawiki.editRecovery.edit') === 'ready') {
let storage = mw.loader.moduleRegistry['mediawiki.editRecovery.edit'].packageExports['storage.js'];
storage.deleteData(mw.config.get('wgPageName'));
storage.closeDatabase();
}
notif = await mw.notify(response.edit.nochange ? 'No change' : [
document.createTextNode('Saved'),
$('<p>').append(
new OO.ui.ButtonWidget({
href: mw.util.getUrl(),
target: '_blank',
label: 'View'
}).$element,
new OO.ui.ButtonWidget({
href: mw.util.getUrl(null, {
diff: response.edit.newrevid || 'cur',
diffonly: 1
}),
target: '_blank',
label: 'Diff'
}).$element,
new OO.ui.ButtonWidget({
href: mw.util.getUrl(null, { action: 'history' }),
target: '_blank',
label: 'History'
}).$element
)[0]
], { tag: 'savenedit' });
} catch (error) {
notif = await mw.notify(error?.error?.info || error || 'Save failed', {
autoHideSeconds: 'long',
tag: 'savenedit',
type: 'error'
});
} finally {
button.setDisabled();
}
});
}());
mw.config.get('wgNamespaceNumber') === 0 &&
mw.config.get('wgAction') === 'view' &&
mw.config.get('wgCategories')?.some(c => c.endsWith(' actors') || c.endsWith(' actresses')) &&
$(() => {
let n = $.escapeSelector(mw.config.get('wgTitle').replace(/ \(.+\)$/, ''));
let $links = $(`.hatnote a[title$="${n} filmography"], .hatnote a[title*="${n} on "], .hatnote a[title*="${n} performances"]`);
if (!$links.length) return;
let titles = {};
$links = $links.filter(function () {
let text = this.textContent;
return !(titles[text] = Object.hasOwn(titles, text));
});
mw.notify(
$links.length === 1
? $links.clone()
: $('<ul>').append($links.clone().wrap('<li>').parent()),
{ autoHideSeconds: 'long' }
);
});
['Recentchanges', 'Recentchangeslinked', 'Watchlist'].includes(mw.config.get('wgCanonicalSpecialPageName')) &&
$.when($.ready, mw.loader.using([
'user.options', 'mediawiki.util', 'mediawiki.api'
])).then(function rcMuter() {
let os = mw.user.options.get('userjs-rcmuter');
let set = new Set(os && os.split('|'));
let save = () => {
let ns = [...set].join('|');
if (ns === mw.user.options.get('userjs-rcmuter')) return;
new mw.Api().saveOption('userjs-rcmuter', ns);
mw.user.options.set('userjs-rcmuter', ns);
$edit.attr('data-rcmuter', set.size);
};
mw.loader.addStyleTag('body:not(.rcmuter-disabled) .rcmuter-muted{display:none !important} .rcmuter-edit::after, .rcmuter-togglemuted::after{content:": " attr(data-rcmuter)}');
let $edit = $('<a>').attr({
class: 'rcmuter-edit',
href: '#',
'data-rcmuter': set.size
}).text('Edit muted').on('click', e => {
e.preventDefault();
mw.loader.using([
'oojs-ui-windows', 'mediawiki.widgets.UsersMultiselectWidget'
]).then(() => OO.ui.getWindowManager().getWindow('message')).then(dialog => {
let multiselect = new mw.widgets.UsersMultiselectWidget({
$overlay: dialog.$overlay,
ipAllowed: true,
selected: [...set]
}).connect(dialog, { change: 'updateSize', reorder: 'updateSize' });
let instance = dialog.open({
message: $([
document.createTextNode('Muted users:'),
multiselect.$element[0]
]),
size: 'medium'
});
instance.opened.then(() => {
setTimeout(() => {
multiselect.focus().menu.toggle(false);
});
});
instance.closed.then(result => {
if (!result || result.action !== 'accept') return;
set = new Set(multiselect.getSelectedUsernames());
save();
mw.notify('Changes will take effect in next load.', {
tag: 'rcmuter'
});
});
});
});
let buttonsShown;
let $toggleButtons = $('<a>').attr('href', '#').text('Show toggle buttons').on('click', function (e) {
e.preventDefault();
if (buttonsShown) {
mw.hook('wikipage.content').remove(addButtons);
$('.rcmuter-toggle').remove();
this.textContent = 'Show toggle buttons';
} else {
mw.hook('wikipage.content').add(addButtons);
this.textContent = 'Hide toggle buttons';
}
buttonsShown = !buttonsShown;
});
let $toggle = $('<a>').attr({
class: 'rcmuter-togglemuted',
href: '#'
}).text('Show muted').on('click', function (e) {
e.preventDefault();
this.textContent = document.body.classList.toggle('rcmuter-disabled')
? 'Hide muted'
: 'Show muted';
});
let $toggleSpan = $('<span>').hide().append($toggle);
mw.util.addSubtitle(
$('<span>').addClass('mw-changeslist-links').append(
$('<span>').append($edit),
$('<span>').append($toggleButtons),
$toggleSpan
)[0]
);
let toggle = function (e) {
e.preventDefault();
let user = $(this)
.closest('.mw-userlink ~ .mw-usertoollinks, .mw-changeslist-line-inner-userLink ~ .mw-changeslist-line-inner-userTalkLink')
.prevAll('.mw-userlink, .mw-changeslist-line-inner-userLink')
.last().text().trim();
if (!user) {
mw.notify(`Can't retrieve the username.`, {
tag: 'rcmuter',
type: 'error'
});
return;
}
let muting = this.parentElement.classList.toggle('rcmuter-unmute');
set[muting ? 'add' : 'delete'](user);
save();
this.textContent = muting ? 'unmute' : 'mute';
mw.notify(`${muting ? 'Muting' : 'Unmuting'} ${user} from next load.`, {
tag: 'rcmuter'
});
};
let addButtons = $content => {
if (!$content.is('#mw-content-text, .mw-changeslist')) {
$content = $('#mw-content-text');
if ($content.has('.rcmuter-toggle').length) return;
}
let $tools = $content.find('.mw-usertoollinks.mw-changeslist-links');
let $muted = $tools.filter('.rcmuter-muted *');
$tools.not($muted).append(
$('<span>').addClass('rcmuter-toggle').append(
$('<a>').attr('href', '#').text('mute').on('click', toggle)
)
);
if (!$muted.length) return;
$muted.append(
$('<span>').addClass('rcmuter-toggle rcmuter-unmute').append(
$('<a>').attr('href', '#').text('unmute').on('click', toggle)
)
);
};
let mutedCount;
let filter = function () {
let muted = set.has(this.textContent);
if (muted) mutedCount++;
return muted;
};
mw.hook('wikipage.content').add($content => {
if (!$content.is('#mw-content-text, .mw-changeslist')) return;
if (!set.size) {
$toggleSpan.hide();
return;
}
mutedCount = 0;
$content.find('.changedby > .mw-userlink:only-child')
.filter(filter).closest('table').addClass('rcmuter-muted');
$content.find('.mw-userlink:not(.changedby > *, .comment *, .rcmuter-muted *)')
.filter(filter).closest('.mw-changeslist-line, table').addClass('rcmuter-muted')
.closest('table.mw-enhanced-rc').find('.changedby > .mw-userlink').filter(filter).addClass('rcmuter-muted');
$toggleSpan.toggle(!!mutedCount);
$toggle.attr('data-rcmuter', mutedCount);
});
});
location.hostname.endsWith('.wikipedia.org') &&
mw.config.get('wgNamespaceNumber') % 2 === 0 &&
// mw.config.get('wgArticleId') &&
mw.config.get('wgPageContentModel') === 'wikitext' &&
$.when($.ready, mw.loader.using('mediawiki.util')).then(function refRenamer() {
if (!document.getElementById('p-tb')) return;
let messages = Object.assign({
portlet: 'RefRenamer',
loading: 'Loading RefRenamer...'
}, window.refrenamerMessages);
let clicked;
mw.util.addPortletLink('p-tb', '#', messages.portlet, 't-refrenamer').firstElementChild.addEventListener('click', e => {
e.preventDefault();
if (clicked) {
if (window.refRenamer) {
window.refRenamer();
}
return;
}
clicked = true;
mw.loader.load('//test.wikipedia.org/w/index.php?title=User:Nardog/sandbox6.js&action=raw&ctype=text/javascript');
mw.notify(messages.loading, {
autoHideSeconds: 'long',
tag: 'refrenamer'
});
});
});
if (['edit', 'submit'].includes(mw.config.get('wgAction'))) {
mw.loader.load('//en.wikipedia.org/w/index.php?title=User:Nardog/ExpandContractions.js&action=raw&ctype=text/javascript', 's');
mw.loader.load('//en.wikipedia.org/w/index.php?title=User:Nardog/Unpipe.js&action=raw&ctype=text/javascript', 's');
}
mw.config.get('wgAction') !== 'history' &&
mw.loader.load('//en.wikipedia.org/w/index.php?title=User:Nardog/CopyCodeBlock.js&action=raw&ctype=text/javascript', 's');
mw.config.exists('wgDiffNewId') &&
mw.config.get('wgDiscussionToolsFeaturesEnabled') &&
(function () {
let data = {}, clickHandler, autoClear, run;
window.dtc = data;
let highlight = revId => {
let ids = data[revId];
if (!ids || !ids.length) return;
mw.loader.moduleRegistry['ext.discussionTools.init'].packageExports['highlighter.js']
.highlightNewComments(mw.dt.pageThreads, true, ids);
if (clickHandler) {
$(document.body).off('click', clickHandler);
return;
}
$._data(document.body, 'events').click.some(o => {
if (String(o.handler).includes('highlighter.clearHighlightTargetComment(')) {
$(document.body).off('click', o.handler);
clickHandler = o.handler;
return true;
}
});
$._data(window, 'events').popstate.some(o => {
if (String(o.handler).includes('highlighter.highlightTargetComment(')) {
$(window).off('popstate', o.handler);
return true;
}
});
};
let scroll = revId => {
let ids = data[revId];
if (!ids || !ids.length) return;
let yToSpan = Object.fromEntries(
ids.map(id => document.getElementById(id)).filter(Boolean)
.map(span => [span.getBoundingClientRect().y, span])
);
let ys = Object.keys(yToSpan);
if (!ys.length) return;
let lower = ys.filter(y => y > 10);
if (!lower.length ||
Math.max(...lower) < document.documentElement.clientHeight
) {
yToSpan[Math.min(...ys)].scrollIntoView();
} else {
yToSpan[Math.min(...lower)].scrollIntoView();
}
};
let scrollToNext = function (e) {
e.preventDefault();
let revId = mw.config.get('wgDiffOldId');
if (!revId || !data[revId]) return;
let i = data[revId].indexOf(
this.closest('[data-mw-thread-id]').dataset.mwThreadId
);
if (i === -1) return;
let next = data[revId][i + 1] || data[revId][0];
document.getElementById(next).scrollIntoView();
};
mw.hook('wikipage.content').add(async $content => {
let revId = mw.config.get('wgDiffOldId');
if (!revId) return;
let param = new URLSearchParams(location.search).get('diffonly');
if (param && param !== '0') return;
if (data[revId]) {
highlight(revId);
return;
}
await mw.loader.using(['ext.discussionTools.init', 'mediawiki.util']);
let begin = Date.parse($('#mw-diff-otitle1 .mw-diff-timestamp').data('timestamp'));
data[revId] = mw.dt.pageThreads.getCommentItems()
.filter(c => c.timestamp > begin).map(c => c.id);
if (!data[revId].length) return;
await new Promise(setTimeout);
highlight(revId);
$content.find('.ext-discussiontools-init-replylink-buttons').filter(function () {
return data[revId].includes(this.dataset.mwThreadId);
}).children('span:last-of-type').before(
' | ',
$('<a>').attr({
href: '#',
role: 'button'
}).text('next').on('click', scrollToNext)
);
if (run || !document.getElementById('p-tb')) return;
run = true;
let portlet = mw.util.addPortletLink('p-tb', '#', 'Scroll to next', 't-scrolltonext');
portlet.firstElementChild.addEventListener('click', e => {
e.preventDefault();
scroll(mw.config.get('wgDiffOldId'));
});
mw.util.addPortletLink('p-tb', '#', 'Toggle highlight', 't-togglehighlight').firstElementChild.addEventListener('click', e => {
e.preventDefault();
autoClear = !autoClear;
if (autoClear) {
$(document.body).on('click', clickHandler)[0].click();
} else {
highlight(mw.config.get('wgDiffOldId'));
}
});
mw.loader.addStyleTag(`#t-scrolltonext{position:fixed;bottom:${portlet.clientHeight}px} #t-togglehighlight{position:fixed;bottom:0}`);
});
}());
mw.config.get('wgNamespaceNumber') === 6 &&
mw.config.get('wgAction') === 'view' &&
mw.hook('wikipage.content').add($content => {
$content.find('.filehistory .mw-usertoollinks-contribs').after(function () {
return [
' | ',
$('<a>').attr('href', `${
mw.config.get('wgScript')
}?title=Special:ListFiles/${
this.pathname.replace(/^.+\//, '')
}&ilshowall=1`).text('uploads')
];
});
});
['edit', 'submit'].includes(mw.config.get('wgAction')) &&
mw.config.get('wgArticleId') &&
mw.config.get('wgPageContentModel') === 'wikitext' &&
$(function () {
if (!$('input[name="wpSection"]').val()) return;
mw.hook('wikipage.content').add(async $content => {
let $refs = $content.find('.mw-ext-cite-warning-sectionpreview_no_text');
if (!$refs.length) return;
let ids = {};
$refs.each(function () {
ids[this.closest('[id]').id.replace(/-\d+$/, '')] = this;
});
let response = await $.get(`/api/rest_v1/page/html/${encodeURIComponent(mw.config.get('wgPageName'))}`);
$($.parseHTML(response)).find('.mw-reference-text').each(function () {
ids[this.id.replace(/^mw-reference-text-|-\d+$/g, '')]?.replaceWith(this);
});
});
});
mw.hook('moremenu.ready').add(config => {
$('#mm-page-purge-cache > a').on('click', e => {
e.preventDefault();
new mw.Api().post({
action: 'purge',
forcelinkupdate: 1,
titles: config.page.name,
formatversion: 2
}).then(() => {
location.href = mw.util.getUrl();
});
});
$('#mm-page-search-search-history-wikiblame > a').on('click', function (e) {
e.preventDefault();
let q = prompt();
if (q === null) return;
let href = this.href;
if (q) {
let removal = q[0] === '!';
if (removal) {
q = q.slice(1);
}
href += '&needle=' + encodeURIComponent(q);
if (removal) {
href += '&binary_search_inverse=on';
}
href += '&force_wikitags=on';
}
open(href, '_blank');
});
$('#mm-page-expand-templates > a').on('click auxclick', function (e) {
if (e.which > 2) return;
e.preventDefault();
let revId = mw.config.get('wgRevisionId') || Number($('input[name=oldid]').val());
let url = revId
? '/w/rest.php/v1/revision/' + revId
: '/w/rest.php/v1/page/' + config.page.encodedName;
$.get(url).then(response => {
$('<form>').attr({
method: 'post',
action: this.href,
target: '_blank'
}).append(
[
['wpInput', response.source],
['wpContextTitle', config.page.name],
['wpRemoveComments', 1]
].map(([n, v]) => $('<input>').attr({
name: n,
type: 'hidden'
}).val(v))
).appendTo(document.body).trigger('submit').remove();
});
});
});
mw.config.get('wgCanonicalSpecialPageName') === 'ApiSandbox' &&
mw.hook('apisandbox.formatRequest').add((...args) => {
args[4].complete = function () {
setTimeout(() => {
mw.hook('wikipage.content').fire($('.oo-ui-pageLayout-active'));
}, 100);
};
});
['edit', 'submit'].includes(mw.config.get('wgAction')) &&
mw.config.get('wgArticleId') &&
mw.config.get('wgPageContentModel') === 'wikitext' &&
$.when($.ready, mw.loader.using('mediawiki.storage')).then(async () => {
let infuseAndCall = (query, method, ...args) => {
let $widget = $(query);
if ($widget.length) {
return OO.ui.infuse($widget)[method](...args);
}
};
let section = $('input[name="wpSection"]').val();
if (section) {
let $textarea = $('#wpTextbox1');
let source = $textarea.prop('defaultValue');
let save = () => {
let newSource = $textarea.textSelection('getContents');
if (newSource === source) {
mw.storage.session.remove('editfullpage');
} else {
mw.storage.session.setObject('editfullpage', [
mw.config.get('wgPageName'),
section,
newSource.trimEnd(),
infuseAndCall('#wpSummaryWidget', 'getValue') || '',
Number(infuseAndCall('#wpMinoreditWidget', 'isSelected')) || 0,
Number(infuseAndCall('#wpWatchthisWidget', 'isSelected')) || 0,
infuseAndCall('#wpWatchlistExpiryWidget', 'getValue') || 'infinite'
]);
}
};
await mw.loader.using(['jquery.textSelection', 'oojs-ui-core']);
setInterval(() => {
mw.requestIdleCallback(save);
}, 3000);
window.addEventListener('beforeunload', save);
return;
}
let data = mw.storage.session.getObject('editfullpage');
mw.storage.session.remove('editfullpage');
console.log(data);
if (!data || data[0] !== mw.config.get('wgPageName')) return;
let isNew = data[1] === 'new';
let isLead = data[1] === '0';
let $textarea = $('#wpTextbox1');
let source = $textarea.prop('defaultValue');
let newSource, start, msg, notifOpts = { autoHideSeconds: 'long' };
let orig = [];
if (isNew) {
await mw.loader.using(['jquery.textSelection', 'oojs-ui-core']);
newSource = source + (data[3] ? '\n== ' + data[3] + ' ==\n\n' : '\n') + data[2] + '\n';
start = source.length;
} else {
await mw.loader.using(['jquery.textSelection', 'oojs-ui-core', 'mediawiki.api']);
let { parse } = await new mw.Api().get({
action: 'parse',
page: mw.config.get('wgPageName'),
prop: 'sections',
wrapoutputclass: '',
disablelimitreport: 1,
disableeditsection: 1,
disabletoc: 1,
formatversion: 2
});
let target = !isLead && parse.sections.find(s => s.index === data[1]);
if (isLead || target) {
let next = parse.sections.find(s => s.index - 1 === Number(data[1]));
newSource = (isLead ? '' : [...source].slice(0, target.byteoffset)).join('') +
data[2] + (next ? '\n\n' + [...source].slice(next.byteoffset).join('') : '\n');
start = isLead ? 0 : target.byteoffset;
} else {
newSource = source + '\n\n' + data[2] + '\n';
start = source.length;
msg = `Section restored. Couldn't find the section. The source is appended at bottom.`;
notifOpts.type = 'warn';
}
orig[0] = infuseAndCall('#wpSummaryWidget', 'getValue');
infuseAndCall('#wpSummaryWidget', 'setValue', data[3]);
}
$textarea.textSelection('setContents', newSource);
orig[1] = infuseAndCall('#wpMinoreditWidget', 'getSelected');
infuseAndCall('#wpMinoreditWidget', 'setSelected', data[4]);
orig[2] = infuseAndCall('#wpWatchthisWidget', 'getSelected');
infuseAndCall('#wpWatchthisWidget', 'setSelected', data[5]);
orig[3] = infuseAndCall('#wpWatchlistExpiryWidget', 'getValue');
infuseAndCall('#wpWatchlistExpiryWidget', 'setValue', data[6]);
setTimeout(() => {
$textarea.textSelection('setSelection', { start });
});
let notif = await mw.notify($([
document.createTextNode(msg || 'Section restored.'),
$('<p>').append(
new OO.ui.ButtonWidget({
flags: 'destructive',
label: 'Discard'
}).on('click', () => {
$textarea.textSelection('setContents', source);
if (orig[0]) {
infuseAndCall('#wpSummaryWidget', 'setValue', orig[0]);
}
infuseAndCall('#wpMinoreditWidget', 'setSelected', orig[1]);
infuseAndCall('#wpWatchthisWidget', 'setSelected', orig[2]);
infuseAndCall('#wpWatchlistExpiryWidget', 'setValue', orig[3]);
notif.close();
}).$element
)[0]
]), notifOpts);
});
mw.config.exists('wgPostEdit') &&
mw.loader.using('mediawiki.storage', () => {
mw.storage.session.remove('editfullpage');
});
mw.config.get('wgAction') === 'history' &&
mw.hook('wikipage.content').add(async $content => {
if (!$content.has('.mw-history-line-updated').length) return;
let href = $content.find('a.mw-history-histlinks-current:not(.mw-history-line-updated a)').attr('href');
if (!href) {
await mw.loader.using(['mediawiki.api', 'mediawiki.util']);
let page = (await new mw.Api().get({
action: 'query',
titles: mw.config.get('wgPageName'),
prop: 'info',
inprop: 'notificationtimestamp',
formatversion: 2
})).query.pages[0];
let rev = (await new mw.Api().get({
action: 'query',
titles: page.title,
prop: 'revisions',
rvprop: 'ids',
rvlimit: 1,
rvstart: Date.parse(page.notificationtimestamp) / 1000 - 1,
formatversion: 2
})).query.pages[0].revisions?.[0].revid;
if (!rev || rev >= page.lastrevid) return;
href = mw.util.getUrl(page.title, { diff: page.lastrevid, oldid: rev });
}
$content.find('.mw-history-compareselectedversions-button').first().after(
' ',
$('<a>').attr({
class: 'unseendiff',
href: href
}).text('unseen')
);
});
(async () => {
let cspn = mw.config.get('wgCanonicalSpecialPageName');
let isBp = cspn === 'Blankpage';
if (!isBp && cspn !== 'Watchlist') return;
await mw.loader.using('mediawiki.util');
let notify = async (text, options, pn) => {
let msg = [document.createTextNode(text)];
if (pn) {
msg.push(
$('<p>').append(
$('<a>').attr('href', mw.util.getUrl(pn)).text(pn),
' ',
$('<span>').addClass('mw-changeslist-links').append(
$('<span>').append(
$('<a>')
.attr('href', mw.util.getUrl(pn, { action: 'edit' }))
.text('edit')
),
$('<span>').append(
$('<a>')
.attr('href', mw.util.getUrl(pn, { action: 'history' }))
.text('history')
)
)
)[0]
);
}
if (isBp) {
await $.ready;
$('#mw-content-text').html(msg);
} else {
return mw.notify(msg, Object.assign(options || {}, {
tag: 'unseendiff'
}));
}
};
let getUrl = async pn => {
await mw.loader.using('mediawiki.api');
let page = (await new mw.Api().get({
action: 'query',
titles: pn,
prop: 'info',
inprop: 'notificationtimestamp',
formatversion: 2
})).query.pages[0];
if (!page.notificationtimestamp) {
notify(`Couldn't get the last seen time.`, {
type: 'warn'
}, pn);
return;
}
let rev = (await new mw.Api().get({
action: 'query',
titles: pn,
prop: 'revisions',
rvprop: 'ids',
rvlimit: 1,
rvstart: Date.parse(page.notificationtimestamp) / 1000 - 1,
formatversion: 2
})).query.pages[0].revisions?.[0].revid;
if (rev === page.lastrevid) {
notify('Already seen.', {
type: 'warn'
}, pn);
return;
}
if (!rev) {
rev = (await new mw.Api().get({
action: 'query',
titles: pn,
prop: 'revisions',
rvprop: 'ids',
rvlimit: 1,
rvstart: Date.parse(page.notificationtimestamp) / 1000 - 1,
rvdir: 'newer',
formatversion: 2
})).query.pages[0].revisions?.[0].revid;
if (!rev) {
notify(`Couldn't get the last seen revision.`, {
type: 'warn'
}, pn);
return;
}
}
if (rev > page.lastrevid) {
notify(`Invalid rev for "${pn}" (rev: ${rev}, lastrevid: ${page.lastrevid})`, {
autoHideSeconds: 'long',
type: 'warn'
}, pn);
return;
}
return mw.util.getUrl(page.title, { diff: page.lastrevid, oldid: rev });
};
if (isBp) {
let pn = mw.config.get('wgTitle').match(/^[^/]+\/unseendiff\/(.+)$/)?.[1];
if (!pn) return;
notify('Loading...', null, pn);
let href = await getUrl(pn);
if (!href) return;
notify('Redirecting...', null, pn);
location.href = href;
return;
}
let handler = async function (e) {
if (e.which > 2) return;
e.preventDefault();
let pn = this.dataset.pn;
if (!pn) {
notify(`Couldn't get the page name.`, {
type: 'error'
});
return;
}
let notifPromise = notify('Loading...', {
autoHideSeconds: 'long'
});
let href = await getUrl(pn);
if (!href) return;
$(`.unseendiff-loader[data-pn="${$.escapeSelector(pn)}"]`).attr({
class: 'unseendiff',
href: href,
target: '_blank'
}).off('click auxclick', handler);
if (e.type === 'auxclick' || e.ctrlKey || e.metaKey || e.shiftKey) {
open(href);
} else {
this.click();
}
(await notifPromise).close();
};
mw.hook('wikipage.content').add($content => {
$content.find(
'.mw-changeslist-src-mw-edit.mw-changeslist-watchedunseen:not(.mw-changeslist-watchedseen) .mw-changeslist-line-inner'
).each(function () {
let pn = this.dataset.targetPage ||
this.closest('[data-target-page]')?.dataset.targetPage ||
this.closest('table.mw-enhanced-rc')?.querySelector('[data-target-page]')?.dataset.targetPage;
if (!pn) return;
$('<span>').append(
$('<a>').attr({
class: 'unseendiff-loader',
href: mw.util.getUrl(`Special:BlankPage/unseendiff/${pn}`),
'data-pn': pn
}).on('click auxclick', handler).text('unseen')
).appendTo(
[...this.querySelectorAll('.mw-pager-tools')].pop() ||
$('<span>').addClass('mw-changeslist-links mw-pager-tools').appendTo(this).before(' ')
);
});
});
})();
['Contributions', 'IPContributions', 'Blankpage'].includes(mw.config.get('wgCanonicalSpecialPageName')) &&
mw.loader.using('mediawiki.util', () => {
let watched = new Set();
let query = async lis => {
let titles = Object.keys(lis).slice(0, 50);
if (!titles.length) return;
await mw.loader.using('mediawiki.api');
let pages = (await new mw.Api().post({
action: 'query',
titles: titles,
prop: 'info',
inprop: 'notificationtimestamp|watched',
formatversion: 2
}, {
headers: { 'Promise-Non-Write-API-Action': 1 }
})).query.pages;
for (let page of pages) {
if (!Object.hasOwn(lis, page.title)) continue;
if (page.watched) {
watched.add(page);
$(lis[page.title]).addClass('watched');
}
if (!page.notificationtimestamp) continue;
let rev = (await new mw.Api().get({
action: 'query',
titles: page.title,
prop: 'revisions',
rvprop: 'ids',
rvlimit: 1,
rvstart: Date.parse(page.notificationtimestamp) / 1000 - 1,
formatversion: 2
})).query.pages[0].revisions?.[0].revid;
if (!rev || rev === page.lastrevid) continue;
if (rev > page.lastrevid) {
mw.notify($([
document.createTextNode('Invalid rev for "'),
$('<a>').attr({
href: mw.util.getUrl(page.title, { action: 'history' }),
target: '_blank'
}).text(page.title)[0],
document.createTextNode(`" (rev: ${rev}, lastrevid: ${page.lastrevid})`),
]), {
autoHideSeconds: 'long',
type: 'warn'
});
continue;
}
$('<span>').append(
$('<a>').attr({
class: 'unseendiff',
href: mw.util.getUrl(page.title, {
diff: page.lastrevid,
oldid: rev
})
}).text('unseen')
).appendTo(
lis[page.title].map(li => (
[...li.querySelectorAll(':scope > .mw-pager-tools')].pop() ||
$('<span>').addClass('mw-changeslist-links mw-pager-tools').appendTo(li).before(' ')
))
);
}
titles.forEach(title => {
delete lis[title];
});
query(lis);
};
mw.hook('wikipage.content').add($content => {
$content.find(
'.mw-contributions-list > li:not(.mw-contributions-current)[data-mw-revid]'
).each(function () {
let link = this.querySelector('a.mw-changeslist-date, a.mw-changeslist-history');
let pn = link ? new URLSearchParams(link.search).get('title') : '';
$('<span>').append(
$('<a>').attr({
class: 'mw-changeslist-diff',
href: mw.util.getUrl(pn, {
diff: 'cur',
oldid: this.dataset.mwRevid
})
}).text('cur')
).appendTo(
[...this.querySelectorAll(':scope > .mw-pager-tools')].pop() ||
$('<span>').addClass('mw-changeslist-links mw-pager-tools').appendTo(this).before(' ')
);
});
if (mw.config.get('wgWikiID') === 'wikidatawiki') return;
let lis = {};
$content.find('.mw-contributions-title').each(function () {
let title = this.textContent;
if (!Object.hasOwn(lis, title)) {
lis[title] = [];
}
lis[title].push(this.closest('li'));
});
Object.keys(lis).forEach(title => {
if (watched.has(title)) {
$(lis[title]).addClass('watched');
delete lis[title];
}
});
query(lis);
});
});
['edit', 'submit'].includes(mw.config.get('wgAction')) &&
mw.loader.load('//test.wikipedia.org/w/index.php?title=User:Nardog/sandbox9.js&action=raw&ctype=text/javascript');
mw.config.get('wgWikiID') === 'metawiki' &&
(async () => {
let css = mw.loader.addStyleTag(`.wishtitle {
font-size: 90%;
font-style: italic;
word-break: break-word;
}
.wishtitle > a {
color: var(--color-warning, #886425);
}
.wishtitle > a:visited {
color: var(--border-color-warning--hover, #735421);
}
.wishtitle-declined > a {
text-decoration: line-through;
}
.wishtitle-declined > a:hover,
.wishtitle-declined > a:focus,
.mw-underline-always .wishtitle-declined > a {
text-decoration: line-through underline;
}
#watchlist-edit-form .wishtitle {
display: inline-block;
}
.mw-search-result-heading > .wishtitle,
.catchangesviewer-table .wishtitle {
display: block;
}
.catchangesviewer-table:has(.wishtitle) {
white-space: wrap;
}`);
let lang = mw.config.get('wgUserLanguage');
let titles;
let loadTitles = async () => {
await mw.loader.using('mediawiki.storage');
titles = titles || mw.storage.getObject('wishtitles');
if (titles?.lang !== lang) {
titles = { lang, w: [], fa: [] };
}
};
let updateTitles = async (crwstatuses, crwcontinue) => {
await mw.loader.using('mediawiki.api');
let params = {
action: 'query',
list: 'communityrequests-wishes',
crwlang: lang,
crwstatuses: crwstatuses,
crwprop: 'title|updated',
crwsort: 'updated',
crwdir: 'ascending',
crwlimit: 'max',
crwcontinue: crwcontinue,
formatversion: 2
};
if (!crwcontinue && !crwstatuses && titles._) {
params.crwcontinue = `|${titles._}|0`;
}
let response = await new mw.Api().get(params);
let wishes = response?.query?.['communityrequests-wishes'];
if (wishes?.length) {
let $span = $('<span>');
wishes.forEach(w => {
let id = w.crwtitle.match(/^Community Wishlist\/W(\d+)/)?.[1];
if (!id) return;
titles.w[id - 1] = $span.html(w.title).text();
if (crwstatuses === 'declined') {
(titles.wd = titles.wd || []).push(id - 1);
}
let faId = w.crfatitle?.match(/^Community Wishlist\/FA(\d+)/)?.[1];
if (!faId) return;
titles.fa[faId - 1] = w.focusareatitle;
});
if (!crwstatuses) {
titles._ = wishes.at(-1).updated.replace(/\D/g, '');
}
}
let expiry = 86400;
if (crwstatuses || crwcontinue) {
let prev = mw.storage.getObject('_EXPIRY_wishtitles');
if (prev) {
expiry = Math.round(Date.now() / 1000) + 86400 - prev;
}
}
mw.storage.setObject('wishtitles', titles, expiry);
crwcontinue = response?.continue?.crwcontinue;
if (crwcontinue) {
await updateTitles(crwstatuses, crwcontinue);
}
};
let getTitle = id => (
id[0] === 'W' ? titles.w[id.slice(1) - 1] : titles.fa[id.slice(2) - 1]
);
let renderTitle = (title, id, tag = 'span') => {
let classes = 'wishtitle';
if (id[0] === 'W' && titles.wd?.includes(id.slice(1) - 1)) {
classes += ' wishtitle-declined';
}
return $(`<${tag}>`).addClass(classes).append(
$('<a>').attr({
href: `/wiki/Community_Wishlist/${id}`,
title: `Community Wishlist/${id}`
}).text(title)
);
};
let callback = ([id, links]) => {
let title = getTitle(id);
if (!title) {
return true;
}
$(links).after(' ', renderTitle(title, id));
};
let selector = '.mw-changeslist-title, ' +
'.mw-changeslist-log-entry > a:not(.mw-userlink), ' +
'.mw-changeslist-line.mw-changeslist-src-mw-categorize :is(.mw-changeslist-line-inner, .mw-changeslist-line-inner-comment, .mw-enhanced-rc-nested) > .comment > a, ' +
'#watchlist-edit-form .cdx-table td > label > a, ' +
'.mw-search-result-heading > a:not(:has(> .ext-communityrequests-entity-link--label)), ' +
'.mw-contributions-title, ' +
'#mw-whatlinkshere-list li > bdi > a, ' +
'.mw-allpages-chunk > li > a, ' +
'.mw-prefixindex-list > li > a, ' +
'.mw-logevent-loglines > li > a, ' +
'#mw-pages li > a, ' +
'.catchangesviewer-table td:nth-child(3) > a';
mw.hook('wikipage.content').add(async $content => {
let links = {};
$content.find('a').each(function () {
if (!this.matches(selector)) return;
let id = this.textContent.match(
/^(?:Talk:|Translations:)?Community Wishlist\/((?:W|FA)\d+)/
)?.[1];
if (!id) return;
(links[id] = links[id] || []).push(this);
});
links = Object.entries(links);
if (!links.length) return;
await loadTitles();
links = links.filter(callback);
if (!links.length) return;
await updateTitles();
links = links.filter(callback);
if (!links.length) return;
await updateTitles('declined');
links.forEach(callback);
});
let pn = mw.config.get('wgRelevantPageName');
let id = pn.match(/^(?:Talk:|Translations:)?Community_Wishlist\/((?:W|FA)\d+)/)?.[1];
if (!id) return;
await $.ready;
let extTitle = document.querySelector('.ext-communityrequests-wish--title');
if (extTitle && $('.mw-pt-languages-selected').attr('lang') === lang) return;
await loadTitles();
let title = getTitle(id);
if (!title) {
await updateTitles();
title = getTitle(id);
if (!title) {
await updateTitles('declined');
title = getTitle(id);
if (!title) return;
}
}
let $title = renderTitle(title, id, 'div');
if (mw.config.get('skin') === 'vector-2022') {
$title.prependTo('.vector-page-toolbar');
} else {
$title.insertAfter('#firstHeading');
}
css.textContent += ' .ext-communityrequests-entity-talk-header{display:none}';
if (extTitle) return;
document.title = document.title.replace(
pn.replaceAll('_', ' '),
`${pn.replace(`Community_Wishlist/${id}`, title)} ($&)`
);
})();
mw.config.get('wgWikiID') === 'metawiki' &&
mw.hook('wikipage.watchlistChange').add(async (isWatched, expiry) => {
if (![0, 1].includes(mw.config.get('wgNamespaceNumber'))) return;
let title = mw.config.get('wgTitle');
if (!/^Community Wishlist\/(?:W|FA)\d+$/.test(title)) return;
if (isWatched) {
await new mw.Api().watch(title + '/Votes', expiry);
mw.notify('Watching /Votes too.');
} else {
await new mw.Api().unwatch(title + '/Votes');
mw.notify('Unwatched /Votes too.');
}
});
['edit', 'submit'].includes(mw.config.get('wgAction')) &&
$(async () => {
let $input = $('#wpTemplateSandboxTemplate');
if (!$input.length) return;
mw.loader.addStyleTag('#templatesandbox-editform .oo-ui-fieldLayout{max-width:50em} #templatesandbox-editform .oo-ui-fieldLayout-field{flex-grow:999}');
let makeTemplateField = () => new OO.ui.FieldLayout(
new mw.widgets.TitleInputWidget({
inputId: 'wpTemplateSandboxTemplate',
name: 'wpTemplateSandboxTemplate',
showMissing: false,
value: $input.val()
}),
{ label: 'Template name:' }
);
if (mw.loader.getState('ext.TemplateSandbox') !== 'registered') {
await mw.loader.using('mediawiki.widgets');
$input.parent().replaceWith(makeTemplateField().$element);
return;
}
let require = await mw.loader.using([
'ext.TemplateSandbox.TemplateSandboxTitleWidget',
'ext.TemplateSandbox.styles', 'jquery.makeCollapsible', 'user.options'
]);
let widget = new (require('ext.TemplateSandbox.TemplateSandboxTitleWidget'))({
$overlay: true,
id: 'wpTemplateSandboxPage',
maxLength: 255,
name: 'wpTemplateSandboxPage',
placeholder: 'Page title',
required: false,
tabIndex: 10,
templateTitleFunc: () => $('#wpTemplateSandboxTemplate').val()
});
widget.$element.attr('data-ooui', '{"_":"mw.widgets.TemplateSandboxTitleWidget"}')
.data('oouiInfused', widget);
let fieldset = new OO.ui.FieldsetLayout({
classes: ['mw-templatesandbox-fieldset', 'mw-collapsed'],
id: 'templatesandbox-editform',
items: [
makeTemplateField(),
new OO.ui.ActionFieldLayout(
widget,
new OO.ui.ButtonInputWidget({
id: 'wpTemplateSandboxPreview',
name: 'wpTemplateSandboxPreview',
label: 'Show preview',
tabIndex: 10,
type: 'submit',
useInputTag: true
}),
{ align: 'top' }
)
],
label: 'Preview page with this template'
});
fieldset.$label.append(' ', $('<span>').addClass('mw-collapsible-toggle-placeholder'));
fieldset.$group.addClass('mw-collapsible-content');
$('#templatesandbox-editform').replaceWith(fieldset.$element.makeCollapsible());
let modules = ['ext.TemplateSandbox'];
if (Number(mw.user.options.get('uselivepreview'))) {
modules.push('ext.TemplateSandbox.preview');
}
mw.loader.load(modules);
});
mw.config.get('wgWikiID') === 'enwiki' &&
mw.config.get('wgCanonicalSpecialPageName') === 'Watchlist' &&
(async () => {
mw.loader.addStyleTag('.xfdnotifier-sublinks::before{content:" ["} .xfdnotifier-sublinks::after{content:"]"} .xfdnotifier-sublinks > span:not(:first-child)::before{content:"\\2009·\\2009"} .mw-portlet.vector-menu[id^="p-xfdnotifier-"] a{display:inline}');
await mw.loader.using(['mediawiki.api', 'mediawiki.Title', 'mediawiki.storage']);
let xfds = [
{
id: 'rm',
label: 'RM',
full: 'Requested moves',
cat: 'Requested moves',
},
{
id: 'rmt',
label: 'RM/T',
full: 'Requested moves (technical)',
page: 'Wikipedia:Requested_moves/Technical_requests',
titleExtractor: $page => (
$page.find(`[data-mw*='"wt":"RMassist/core"']`).closest('li').map(function () {
return this.querySelector('a[rel="mw:WikiLink"]')?.title;
}).get()
)
},
{
id: 'afd',
label: 'AfD',
full: 'Articles for deletion',
cat: 'Articles for deletion'
},
{
id: 'mfd',
label: 'MfD',
full: 'Miscellaneous for deletion',
cat: 'Miscellaneous pages for deletion'
},
{
id: 'tfd',
label: 'TfD',
full: 'Templates for deletion',
cat: 'Templates for deletion'
},
{
id: 'tfm',
label: 'TfM',
full: 'Templates for merging',
cat: 'Templates for merging'
},
{
id: 'cfd',
label: 'CfD',
full: 'Categories for deletion',
cat: 'Categories for deletion'
},
{
id: 'cfr',
label: 'CfR',
full: 'Categories for renaming',
cat: 'Categories for renaming'
},
{
id: 'cfsr',
label: 'CfSR',
full: 'Categories for speedy renaming',
cat: 'Categories for speedy renaming'
},
{
id: 'cfm',
label: 'CfM',
full: 'Categories for merging',
cat: 'Categories for merging'
},
{
id: 'cfs',
label: 'CfS',
full: 'Categories for splitting',
cat: 'Categories for splitting'
},
{
id: 'cfl',
label: 'CfL',
full: 'Categories for listifying',
cat: 'Categories for listifying'
},
{
id: 'cfc',
label: 'CfC',
full: 'Categories for conversion',
cat: 'Categories for conversion'
},
{
id: 'cfgd',
label: 'CfGD',
full: 'Categories for general discussion',
cat: 'Categories for general discussion'
},
{
id: 'ffd',
label: 'FfD',
full: 'Files for discussion',
cat: 'Wikipedia files for discussion'
},
{
id: 'rfd',
label: 'RfD',
full: 'Redirects for discussion',
cat: 'All redirects for discussion'
},
{
id: 'prod',
label: 'PROD',
full: 'Articles proposed for deletion',
cat: 'All articles proposed for deletion'
}
];
window.xfd = xfds;
let queryTitles = async (xfd, titles) => {
if (!titles.length) return;
let response = await new mw.Api().get({
action: 'query',
titles: titles.slice(0, 50),
prop: 'info',
inprop: 'watched',
formatversion: 2
});
response?.query?.pages?.forEach(p => {
if (p.watched) {
xfd.pages.push(p.title);
}
});
await queryTitles(xfd, titles.slice(50));
};
let queryPage = async xfd => {
let $page = $($.parseHTML(await $.get(
`https://en.wikipedia.org/w/rest.php/v1/page/${encodeURIComponent(xfd.page)}/html`
)));
await queryTitles(xfd, xfd.titleExtractor($page));
};
let queryCat = async (xfd, gcmcontinue) => {
let response = await new mw.Api().get({
action: 'query',
prop: 'info|categories',
inprop: 'watched',
clprop: 'sortkey',
clcategories: `Category:${xfd.cat}`,
generator: 'categorymembers',
gcmtitle: `Category:${xfd.cat}`,
gcmlimit: 'max',
gcmsort: 'timestamp',
gcmdir: 'older',
gcmcontinue: gcmcontinue,
formatversion: 2
});
response?.query?.pages?.forEach(p => {
if (p.watched && p.categories?.[0]?.sortkeyprefix !== ' ') {
xfd.pages.push(p.title);
}
});
if (response?.continue?.gcmcontinue) {
await queryCat(xfd, response.continue.gcmcontinue);
}
};
let show = async (xfd, lastId, isCache) => {
if (xfd.portlet && isCache) return;
let portletId = 'p-xfdnotifier-' + xfd.id;
if (xfd.portlet) {
$(xfd.portlet).find('ul').empty();
if (!xfd.pages.length) return;
} else {
await $.ready;
xfd.portlet = mw.util.addPortlet(portletId, xfd.label, '#' + lastId);
}
let $label = $(`#${portletId}-label`).attr('title', xfd.full);
if (xfd.page) {
$label.wrapInner($('<a>').attr('href', mw.util.getUrl(xfd.page)));
}
xfd.pages.forEach(p => {
let t = mw.Title.newFromText(p);
let isTalk = t.isTalkPage();
let $other = $('<a>').attr({
href: t[isTalk ? 'getSubjectPage' : 'getTalkPage']().getUrl(),
title: isTalk ? 'subject' : 'talk'
}).text(isTalk ? 's' : 't');
let link = mw.util.addPortletLink(portletId, t.getUrl(), p).querySelector('a');
$('<span>').addClass('xfdnotifier-sublinks').append(
$('<span>').append($other),
$('<span>').append(
$('<a>').attr({
href: t.getUrl({ action: 'history' }),
title: 'history'
}).text('h')
)
).insertAfter(link);
});
};
mw.hook('wikipage.content').add(mw.util.throttle(async () => {
let cache = mw.storage.getObject('xfdnotifier') || {};
let lastId = 'p-tb';
for (let xfd of xfds) {
let portletId = 'p-xfdnotifier-' + xfd.id;
let now = Math.floor(Date.now() / 1000);
if (now - cache[xfd.id]?.[0] < 600) {
xfd.pages = cache[xfd.id].slice(1);
await show(xfd, lastId, true);
lastId = portletId;
continue;
}
xfd.pages = [];
if (xfd.cat) {
await queryCat(xfd);
} else if (xfd.page) {
await queryPage(xfd);
}
cache[xfd.id] = [now, ...xfd.pages];
mw.storage.setObject('xfdnotifier', cache, 604800);
await show(xfd, lastId);
lastId = portletId;
}
}, 1800000));
})();
8q2q2pdplaur6d3a47eb2hpnlykkqgz
Talk:666
1
119269
739034
735325
2026-04-21T15:54:56Z
MPostoronca-WMF
72719
739034
wikitext
text/x-wiki
this is an edit
test
== afsdf ==
{{slink|444|fsdaf}} [[User:Newslinger|Newslinger]] ([[User talk:Newslinger|talk]]) 02:23, 18 March 2021 (UTC)
:Wow, you are talking here before that? [[User:Thingofme|Thingofme]] ([[User talk:Thingofme|talk]]) 10:42, 30 October 2021 (UTC)
== test ==
test [[Special:Contributions/~2026-18067-60|~2026-18067-60]] ([[User talk:~2026-18067-60|talk]]) 09:45, 26 March 2026 (UTC)
s36ip15nlemkrnee1nh6v1fngxxlt7m
User:SongVĩ.Bot II
2
124239
739037
738939
2026-04-21T17:00:14Z
SongVĩ.Bot II
52414
[[User:SongVĩ.Bot II|Task 0]]: Đã 1576 ngày...
739037
wikitext
text/x-wiki
Cập nhật lần cuối: 22-04-2026
Đã 1576 ngày...
r8rxsjhjjpmt041rv59ozvxgj5pdpkm
MediaWiki:GrowthMentors.json
8
127601
739017
738001
2026-04-21T13:24:07Z
Vincatest6
47176
/* growthexperiments-manage-mentors-summary-remove-self-with-reason:Vincatest6|testing mentorship reassignment */
739017
json
application/json
{
"Mentors": {
"49911": {
"message": null,
"weight": 2,
"username": "About672599"
},
"44042": {
"message": null,
"weight": 2,
"username": "Aseleste"
},
"50263": {
"message": "Testing",
"weight": 0,
"username": "DreZhsh"
},
"30874": {
"message": "Testing",
"weight": 2,
"username": "Elli"
},
"27425": {
"message": "Hello and welcome! I'll be happy to help you!",
"weight": 0,
"username": "Etonkovidova (WMF)"
},
"51711": {
"message": "Test",
"weight": 2,
"username": "GOnyeahialam (WMF)"
},
"752": {
"message": "I will be happy to explore together all these new features!",
"weight": 1,
"username": "Geraki"
},
"1": {
"message": "Sup?",
"weight": 2,
"username": "Jon Harald Søby"
},
"39901": {
"message": "Welcome to Wikipedia! I love editing about music, but you can ask me anything.",
"weight": 2,
"username": "MMiller (WMF)"
},
"43912": {
"message": "test222222222222222",
"weight": 2,
"username": "Martin Urbanec (WMF)"
},
"43610": {
"message": "Test",
"weight": 4,
"username": "Paloi Sciurala"
},
"39808": {
"message": "Testing",
"weight": 2,
"username": "Nemoralis"
},
"50179": {
"message": "Uh-huh?",
"weight": 2,
"username": "NguoiDungKhongDinhDanh"
},
"48882": {
"message": "Hi, Welcome to Wikipedia!",
"weight": 2,
"username": "OTichonova (WMF)"
},
"34206": {
"message": "Ask me about design stuff",
"weight": 2,
"username": "RHo (WMF)"
},
"51965": {
"message": "Test wew",
"weight": 0,
"username": "SGimeno (WMF)"
},
"29210": {
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.啊啊啊",
"weight": 1,
"username": "Stang"
},
"12061": {
"message": "Blah blah blah",
"weight": 2,
"username": "Suffusion of Yellow"
},
"50053": {
"message": "Wikipedia:Requests/Help desk/Mentors to become a mentor. \u003Cimg src=1 onerror=\"alert()\"\u003E\u003C/img\u003E",
"weight": 2,
"username": "Sungodtemple"
},
"21799": {
"message": "test porpoise",
"weight": 2,
"username": "Tamzin"
},
"51768": {
"message": "Heh, hey!",
"weight": 2,
"username": "Vasmar1"
},
"1639": {
"message": null,
"weight": 2,
"username": "Wargo"
},
"29926": {
"message": "This experienced user knows you're new and can help you with editing.",
"weight": 1,
"username": "Zilant17"
},
"52081": {
"message": "testing the mentor features",
"weight": 4,
"username": "Zippybonzo"
},
"48247": {
"message": null,
"weight": 0,
"username": "Asartea"
},
"40269": {
"message": "test test test ",
"weight": 2,
"username": "KHarlan (WMF)"
},
"51514": {
"message": null,
"weight": 0,
"username": "Mentor dashboard usability test"
},
"49608": {
"message": null,
"weight": 0,
"username": "SixthGrave"
},
"20771": {
"message": null,
"weight": 0,
"username": "Tacsipacsi"
},
"46860": {
"message": null,
"weight": 0,
"username": "Valcio"
},
"101": {
"message": null,
"weight": 0,
"username": "Xaosflux"
},
"21990": {
"message": null,
"weight": 0,
"username": "Xiplus"
},
"44996": {
"message": "Yaaaay!",
"weight": 2,
"username": "Yahya"
},
"28192": {
"message": "test",
"weight": 2,
"username": "Abbe98"
},
"29752": {
"message": "Hello testers! ",
"weight": 2,
"username": "Trizek (WMF)"
},
"16495": {
"message": "Ffgg",
"weight": 2,
"username": "Iluvatar"
},
"3312": {
"message": "123",
"weight": 2,
"username": "Iniquity"
},
"49609": {
"message": "I will be happy to help!",
"weight": 4,
"username": "Neriah"
},
"53741": {
"message": "Hi",
"weight": 2,
"username": "Syunsyunminmin"
},
"45556": {
"message": "Hello, how can I help you today?",
"weight": 2,
"username": "Dyolf77 (WMF)"
},
"51538": {
"message": null,
"weight": 2,
"username": "Kylemaverick"
},
"37377": {
"message": "I am testing this feature 🙃",
"weight": 2,
"username": "Ата"
},
"28876": {
"message": "test",
"weight": 2,
"username": "Roan Kattouw (WMF)"
},
"52574": {
"message": "kjhjkh",
"weight": 2,
"username": "BMartinezCalvo (WMF)"
},
"43722": {
"message": null,
"weight": 2,
"username": "Ameisenigel"
},
"54146": {
"message": null,
"weight": 2,
"username": "Chqaz"
},
"54035": {
"message": "Hello testers, I'll be happy to help you!",
"weight": 0,
"username": "KStoller-WMF"
},
"61446": {
"message": "(just testing things, nevermind)",
"weight": 0,
"username": "MGrosse-WMF"
},
"31153": {
"message": "test",
"weight": 2,
"username": "Jack who built the house"
},
"46247": {
"message": "Hello! ",
"weight": 2,
"username": "Vincatest9"
},
"19016": {
"message": "Test wiki",
"weight": 2,
"username": "Jdlrobson"
},
"50416": {
"message": "Hello! I'm a test mentor. 123",
"weight": 1,
"username": "Zilant1b"
},
"34459": {
"message": null,
"weight": 0,
"username": "Jon Harald Søby (WMNO)"
},
"53420": {
"message": null,
"weight": 0,
"username": "JFernandez-WMF"
},
"24817": {
"message": "Test mentoring",
"weight": 0,
"username": "MusikAnimal"
},
"60101": {
"message": "Hi!",
"weight": 4,
"username": "Sunny Cryolite"
},
"55545": {
"message": "This experienced user knows you're new and can help you with editing.",
"weight": 4,
"username": "Martin Urbanec (public)"
},
"29017": {
"message": null,
"weight": 2,
"username": "Martin Urbanec"
},
"61465": {
"message": "Hi! I'm a WMF staff member and would like to help you with your contributions to Wikipedia!",
"weight": 2,
"username": "SPerry-WMF"
},
"34390": {
"message": "jhgnhg",
"weight": 2,
"username": "Samwalton9 (WMF)"
},
"70274": {
"message": null,
"weight": 2,
"username": "Vinca11sep01"
},
"43668": {
"message": "test",
"weight": 2,
"username": "Luky001"
},
"71432": {
"message": "mentor schmentor",
"weight": 0,
"username": "Dot.py"
},
"72171": {
"message": "Hello, and welcome! I'm SimpleObjects-9ei, and you can test stuff here. Have any questions? Just ask me.",
"weight": 4,
"username": "SimpleObjects-9ei"
}
}
}
n924nzfxd8iclhg4oq9bv3ivvf3g9cw
Wikipedia:Village pump/topic list
4
146208
739069
739016
2026-04-22T05:41:17Z
Cewbot
33876
[[User:Cewbot/log/20170915/configuration|Generate topic list: 6 topics]]
739069
wikitext
text/x-wiki
<!-- This page is auto-generated by bot. Please contact the bot operator to improve the tool. -->
{| class="wikitable sortable mw-collapsible" style="float:left;"
|-
! data-sort-type="number" style="font-weight: normal;" | <small>#</small> !! 💭 Title !! <span title="Count of comments">💬</span> !! <span title="Count of peoples in discussion">👥</span> !! 🙋 Last editor !! data-sort-type="isoDate" | <span title="Date/Time">🕒 <small>(UTC)</small></span>
|-
| style="text-align: right;" | 1
| [[Wikipedia:Village pump#Script|Script]]
| style="text-align: right;" | 8
| style="text-align: right;" | 5
| style="background-color: #bbb;" | [[User:LuniZunie|LuniZunie]]
| style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2025-11-09T16:47:00.000Z" | 2025-11-09 <span style="color: blue;">16:47</span>
|-
| style="text-align: right;" | 2
| [[Wikipedia:Village pump#Report_concerning_Tanbiruzzammn|Report concerning Tanbiruzzammn]]
| style="text-align: right;" | 2
| style="text-align: right;" | 2
| style="background-color: #bbb;" | [[User:Barras|Barras]]
| style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2025-12-09T21:45:00.000Z" | 2025-12-09 <span style="color: blue;">21:45</span>
|-
| style="text-align: right;" | 3
| [[Wikipedia:Village pump#Report_concerning_Bucheon|Report concerning Bucheon]]
| style="text-align: right;background-color: #fcc;" | 1
| style="text-align: right;background-color: #fcc;" | 1
| style="background-color: #bbb;" | [[User:PieWriter|PieWriter]]
| style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-02-19T10:25:00.000Z" | 2026-02-19 <span style="color: blue;">10:25</span>
|-
| style="text-align: right;" | 4
| [[Wikipedia:Village pump#Versions_and_dates|Versions and dates]]
| style="text-align: right;" | 2
| style="text-align: right;background-color: #fcc;" | 1
| style="background-color: #bbb;" | [[Special:Contributions/~2026-13668-13|<span style="color: #c20;">~2026-13668-13</span>]]
| style="background-color: #bbb;" data-sort-type="isoDate" data-sort-value="2026-03-03T06:17:00.000Z" | 2026-03-03 <span style="color: blue;">06:17</span>
|-
| style="text-align: right;" | 5
| style="max-width: 24em" | <small>[[Wikipedia:Village pump#Upcoming_Wikimedia_Café_meetup_regarding_the_the_2026-2027_Wikimedia_Foundation_Annual_Plan|Upcoming Wikimedia Café meetup regarding the the 2026-2027 Wikimedia Foundation Annual Plan]]</small>
| style="text-align: right;background-color: #fcc;" | 1
| style="text-align: right;background-color: #fcc;" | 1
| style="background-color: #ddd;" | [[User:Pine|Pine]]
| style="background-color: #ddd;" data-sort-type="isoDate" data-sort-value="2026-03-30T03:46:00.000Z" | 2026-03-30 <span style="color: blue;">03:46</span>
|-
| style="text-align: right;" | 6
| [[Wikipedia:Village pump#Changes_to_electionadmin_userright|Changes to electionadmin userright]]
| style="text-align: right;" | 3
| style="text-align: right;" | 3
| [[User:Robertsky|Robertsky]]
| data-sort-type="isoDate" data-sort-value="2026-04-21T05:40:00.000Z" | 2026-04-21 <span style="color: blue;">05:40</span>
|}
{| class="wikitable mw-collapsible mw-collapsed" style="float: left; margin-left: .5em;;{{#if:{{{no_time_legend|}}}|display:none;|}}"
! title="From the latest bot edit" | Legend
|-
| style="background-color: #efe;" |
* In the last hour
|-
| style="background-color: #eef;" |
* In the last day
|-
| |
* In the last week
|-
| style="background-color: #ddd;" |
* In the last month
|-
| style="background-color: #bbb;" |
* More than one month
|-
! Manual settings
|-
| style="max-width: 12em;" | <small>When exceptions occur,<br />please check [[User:Cewbot/log/20170915/configuration|the setting]] first.</small>
|-
|}
{{Clear}}
kq36onm99hy32echx7xzln9cna437wy
Vorlage:Coordinate
0
148889
739052
559543
2026-04-22T00:03:43Z
InternetArchiveBot
34092
Rescuing 1 sources and tagging 0 as dead.) #IABot (v2.0.9.5
739052
wikitext
text/x-wiki
{{Infobox Gemeinde in Deutschland
|Art = Stadt
|Wappen = Reinbek Wappen.svg
|Breitengrad = 53.510211
|Längengrad = 10.250318
|Lageplan = Reinbek in OD.svg
|Bundesland = Schleswig-Holstein
|Kreis = Stormarn
|Höhe = 27
|PLZ = 21465
|Vorwahl = 040, 04104
|Gemeindeschlüssel = 01062060
|LOCODE = DE REI
|Gliederung = [[Liste der Bezirke und Stadtteile Reinbeks|6 statistische Bezirke und 22 Stadtteile]]
|Straße = Hamburger Straße 5–7
|Website = [https://www.reinbek.de/ www.reinbek.de]
|Bürgermeister = [[Björn Warmer]]
|Partei = SPD
}}
'''Reinbek''' ([[Niederdeutsche Sprache|niederdeutsch]] ''Reinbeek''), in der südlichen [[Geest]] [[Schleswig-Holstein]]s gelegen, ist mit etwa 28.000 Einwohnern die zweitgrößte Stadt im [[Kreis Stormarn]]. Die [[Mittelstadt]] liegt im östlichen Ballungsraum [[Hamburg]]s und gehört zur [[Metropolregion Hamburg]].
== Geografie ==
Die Ost- und Südgrenze Reinbeks bildet die zum ''Mühlenteich'' aufgestaute, naturgeschützte [[Bille]]. Die zwischen den Ortsteilen liegenden Flächen werden zum Teil noch landwirtschaftlich genutzt. Geprägt vom angrenzenden [[Sachsenwald]], bietet Reinbek ein grünes, erholsames Stadtbild. Ein Großteil der Stadt ist mit Einzelhäusern bebaut, das Gebiet rings um den Täby-Platz und das Paul Luckow-Stadion besteht zum großen Teil aus mehrstöckigen Mietshäusern, die im Stil der 1960er Jahre erbaut wurden. Das höchste von ihnen, das Sachsenwald-Hochhaus mit 20 Stockwerken, befindet sich in der Hamburger Straße. Zu Reinbek gehören die Stadtteile ''Alt-Reinbek'', [[Hinschendorf]], [[Schönningstedt]], [[Neuschönningstedt]], [[Ohe (Reinbek)|Ohe]] mit [[Büchsenschinken]] und das jüngere Neubaugebiet [[Krabbenkamp]] (→[[Liste der Bezirke und Stadtteile Reinbeks]]). Direkt angrenzend liegen die Hamburger Stadtteile [[Hamburg-Bergedorf|Bergedorf]] und [[Hamburg-Lohbrügge|Lohbrügge]].
== Geschichte ==
{{Hauptartikel|Geschichte der Stadt Reinbek}}
Von der Besiedlung des heutigen Reinbeker Gebietes in bereits vorgeschichtlicher Zeit zeugen zahlreiche [[Hügelgrab|Hügelgräber]]. Die erste urkundlich überlieferte Erwähnung Reinbeks datiert allerdings erst auf das Jahr 1238 und geht auf die Gründung des gleichnamigen [[Zisterzienserinnen]]klosters (siehe [[Kloster Reinbek]]) zurück. Die ältesten bekannten Schreibformen des Ortsnamens sind ''(ville) Reinebec'' (1238), ''(in) Reynebeke'' (1309 und 1350), ''(to deme) Reynenbeke'' (1400) und ''(tome) Rynenbeke'' (1466); der Name wird als Kompositum aus dem Grundwort ''bek'' für „Bach“ und dem Adjektiv „rein“ als Bestimmungswort gedeutet.<ref>[[Wolfgang Laur]]: ''Historisches Ortsnamenlexikon von Schleswig-Holstein'', 2. Aufl., S. 538.</ref> Nach der Zerstörung des Klosters (1534) gewann der Ort erst mit dem Bau der Schlossanlage (1572) wieder an Bedeutung.
[[Datei:Mühlenteich und Reinbeker Schloss im Winter.jpg|mini|Der Mühlenteich und das Reinbeker Schloss im Winter]]
Die Ansiedlung von Handwerkern im späten 18. Jahrhundert brachte endlich wirtschaftliches Wachstum. Einen entscheidenden Impuls für die Entwicklung des Ortes gab jedoch der Bau der Eisenbahnstrecke zwischen Hamburg und [[Berlin]] (1846): Reinbek wurde vorübergehend zum Kurort und beliebten Ausflugsziel. Die alte Schreibweise „Reinbeck“ wurde am 1. September 1877 durch eine Anordnung über die einheitliche Regelung der Schreibweise für Ortsnamen von der Provinzialregierung in Schleswig in „Reinbek“ geändert.
Zum Ende des [[Zweiter Weltkrieg|Zweiten Weltkrieges]] wurde Deutschland schrittweise besetzt. Am 3. Mai 1945 besetzten britischen Truppen auch Reinbek, das benachbarte [[Glinde]] sowie den letzten Teil des noch unbesetzten [[Kreis Stormarn|Stormarns]].<ref>[[Hamburger Abendblatt]]: [http://www.abendblatt.de/region/stormarn/article205288543/Vor-siebzig-Jahren-kapitulierte-die-Stadt-Ahrensburg.html Kriegsende. Vor siebzig Jahren kapitulierte die Stadt Ahrensburg], vom: 2. Mai 2015; abgerufen am: 31. Mai 2017</ref> Des Weiteren begann am Nachmittag des Tages auch die Besetzung [[Hamburg]]s, die zuvor in der [[Villa Möllering]] bei [[Lüneburg]] vereinbart worden war. Einen Tag später unterschrieb zudem [[Hans-Georg von Friedeburg]] im Auftrag des letzten [[Reichspräsident]]en [[Karl Dönitz]], der sich zuvor mit der [[Regierung Dönitz|letzten Reichsregierung]] in den [[Sonderbereich Mürwik]] abgesetzt hatte, die [[Teilkapitulation der Wehrmacht für Nordwestdeutschland, Dänemark und die Niederlande]].<ref>[https://web.archive.org/web/20131104080252/http://www.volksbund.de/fileadmin/redaktion/BereichInfo/Textsammlungen/Ausstellungen/0400_ausstellung_timeloberg/Timeloberg.pdf Die Kapitulation auf dem Timeloberg] (PDF, 16. S.; 455 kB)</ref> Die [[Bedingungslose Kapitulation der Wehrmacht]] folgte am 8. Mai 1945. Zum Kriegsende erlebte Reinbek einen verstärkten Zuzug von Flüchtlingen und durch Kriegseinwirkung obdachlos gewordenen Hamburgern.
Seit den 1960er Jahren wurden mehrere Gewerbegebiete erschlossen und erweitert. Am 28. Juni 1952 erhielt Reinbek das [[Stadtrecht]]. Am 1. Januar 1974 wurden die Gemeinde [[Schönningstedt]] (mit [[Neuschönningstedt]] und [[Ohe (Reinbek)|Ohe]]) sowie ein Teil der Gemeinde Glinde mit damals etwa 100 Einwohnern und ein Teil der aufgelösten Gemeinde Stemwarde eingegliedert.<ref>{{Literatur | Herausgeber = Statistisches Bundesamt | Titel = Historisches Gemeindeverzeichnis für die Bundesrepublik Deutschland. Namens-, Grenz- und Schlüsselnummernänderungen bei Gemeinden, Kreisen und Regierungsbezirken vom 27. Mai 1970 bis 31. Dezember 1982 | Jahr = 1983 | Verlag = W. Kohlhammer GmbH | Ort = Stuttgart/Mainz | ISBN = 3-17-003263-1 | Seiten = 186}}</ref> Im Jahre 1978 kam das bisher landwirtschaftlich genutzte Gebiet Krabbenkamp, das vormals zu Schönningstedt gehörte, als weiterer Stadtteil hinzu.
== Religion ==
Reinbek gehörte ursprünglich zum [[Kirchspiel]] [[Kirchsteinbek|Steinbek]], bis es 1894 zu einer eigenständigen [[Evangelisch-lutherische Kirchen|evangelisch-lutherischen]] [[Kirchengemeinde]] wurde. Die [[Neogotik|neogotische]] Kirche (heute Maria-Magdalenen-Kirche) wurde 1901 errichtet. 1908 gründete sich die [[Römisch-katholische Kirche|römisch-katholische]] Kirchengemeinde, die 1953 die Herz-Jesu-Kirche erbauen ließ. In Reinbek sind 44 % der Bevölkerung evangelisch und 9 % katholisch, 26 % gehören anderen Konfessionen an, 22 % sind ohne Religionszugehörigkeit. Die bedeutendsten Gemeinden der Stadt sind:
* [[Ansgar von Bremen|Ansgar]]-Kirchengemeinde Schönningstedt-Ohe (evangelisch-lutherisch)
* Kirchengemeinde [[Gethsemane]] Neuschönningstedt (evangelisch-lutherisch)
* [[Maria-Magdalenen-Kirche (Reinbek)|Maria-Magdalenen-Kirche]] (evangelisch-lutherisch)
* [[Nathan-Söderblom-Kirche (Reinbek)|Nathan-Söderblom-Kirche]] (evangelisch-lutherisch)
* [[Herz-Jesu-Kirche (Reinbek)|Herz-Jesu]]-Gemeinde (römisch-katholisch)
* [[Evangelisch-Freikirchliche Gemeinde]] ([[Baptisten]])
== Politik ==
=== Stadtvertretung ===
Die letzten drei Kommunalwahlen [[Kommunalwahlen in Schleswig-Holstein 2018|am 6. Mai 2018]], [[Kommunalwahlen in Schleswig-Holstein 2013|am 26. Mai 2013]]<ref>{{Webarchiv|url=http://www.reinbek.de/wahlen/KW2013.html |wayback=20160304064515 |text=Archivierte Kopie}}</ref> und [[Kommunalwahlen in Schleswig-Holstein 2008|am 25. Mai 2008]]<ref>{{Cite web |title=Archived copy |url=http://www.reinbek.de/files/Wahlen/GKW_25052008.pdf#page=10 |access-date=2023-02-21 |archive-date=2016-03-04 |archive-url=https://web.archive.org/web/20160304074748/http://www.reinbek.de/files/Wahlen/GKW_25052008.pdf#page=10 }}</ref> führten zu folgenden Ergebnissen:
{| class="wikitable"
| colspan="2" | '''Parteien und Wählergemeinschaften'''
| align="center" | '''%<br />2018'''
| align="center" | '''Sitze<br />2018'''
| align="center" | '''%<br />2013'''
| align="center" | '''Sitze<br />2013'''
| align="center" | '''%<br />2008'''
| align="center" | '''Sitze<br />2008'''
| rowspan="12" |{{Wahldiagramm
| LAND = DE
| TITEL = Kommunalwahl 2018
| JAHRALT = 2013
| JAHRNEU = 2018
| GUV = ja
| PARTEI1 = CDU
| ERGEBNIS1 = 27.5
| ERGEBNISALT1 = 30.7
| PARTEI3 = SPD
| ERGEBNIS3 = 20.7
| ERGEBNISALT3 = 26.6
| PARTEI2 = GRÜNE
| ERGEBNIS2 = 22.1
| ERGEBNISALT2 = 17.2
| PARTEI5 = Forum21
| ERGEBNIS5 = 11.0
| ERGEBNISALT5 = 13.2
| FARBE5 = 0000FF
| PARTEI4 = FDP
| ERGEBNIS4 = 17.0
| ERGEBNISALT4 = 10.9
| PARTEI6 = [[Klaus-Peter Puls|Puls]]
| ERGEBNIS6 = 1.7
| ERGEBNISALT6 = 1.5
| FARBE6 = CCCCCC
}}
| rowspan="12" |{{Sitzverteilung
| Land = DE
| Überschrift = Sitzverteilung in der Stadtverordnetenversammlung
| SPD|Grüne|Forum21|Puls|FDP|CDU|
| Legende = ja
| SPD = 6
| Grüne = 7
| Forum21 = 3
| Puls = 1
| FDP = 5
| CDU = 9
| Forum21 Farbe = 0000FF
| Puls Farbe = CCCCCC
| Puls Link = [[Klaus-Peter Puls|Puls]]
}}
|- style="text-align:right"
| style="text-align:left" | CDU
| style="text-align:left" | [[Christlich Demokratische Union Deutschlands]]
| 27,5
| 9
| 30,7
| 10
| 33,6
| 13
|- style="text-align:right"
| style="text-align:left" | SPD
| style="text-align:left" | [[Sozialdemokratische Partei Deutschlands]]
| 20,7
| 6
| 26,6
| 8
| 24,3
| 9
|- style="text-align:right"
| style="text-align:left" | GRÜNE
| style="text-align:left" | [[Bündnis 90/Die Grünen]]
| 22,1
| 7
| 17,2
| 5
| 15,4
| 5
|- style="text-align:right"
| Forum21
| Forum21
| 11,0
| 3
| 13,2
| 4
| 13,0
| 4
|- style="text-align:right"
| style="text-align:left" | FDP
| style="text-align:left" | [[Freie Demokratische Partei]]
| 17,0
| 5
| 10,9
| 3
| 13,8
| 5
|- style="text-align:right"
| style="text-align:left" | Puls
| style="text-align:left" | Einzelbewerber [[Klaus-Peter Puls]]<ref>http://www.bergedorfer-zeitung.de/printarchiv/reinbek/article188444/Kommunalwahl-am-26-Mai-2013-Vorstellung-der-Reinbeker-Kandidaten-Wahlkreis-13.html</ref><ref>http://www.abendblatt.de/region/stormarn/article115057641/Klaus-Peter-Puls-tritt-aus-der-SPD-aus.html</ref>
| 1,7
| 1
| 1,5
| 1
| —
| —
|- class="hintergrundfarbe5" style="text-align:right"
| colspan="2" style="text-align:left" | '''gesamt'''
| '''100,0'''
| '''31'''
| '''100,0'''
| '''31'''
| '''100,0'''
| '''36'''
|-
! colspan="2" style="text-align:left" | Wahlbeteiligung in %
! colspan="2" |
! colspan="2" | 45,5
! colspan="2" |
|}
[[Datei:Reinbeker Rathaus.JPG|mini|Reinbeker Rathaus]]
=== Bürgermeister ===
<!-- Amtsvorgänger bitte mit Amtszeit und Partei nachtragen -->
{| class="wikitable"
! colspan="2" | Amtszeit !! rowspan="2" | Name
|-
! von !! bis
|-
| 17. Februar 1931 || 13. September 1945
| Eduard Claußen (NSDAP)<ref>[https://www.museumsverein-reinbek.de/wp-content/uploads/2018/06/Eduard-Claussen.pdf ''Eduard Claußen''], museumsverein-reinbek.de</ref><ref>Claußen half trotz seiner NSDAP-Zugehörigkeit im Rahmen seiner Möglichkeiten mehreren jüdischen Einwohnern und sorgte dafür, dass Reinbek kampflos den Engländern übergeben wurde, vgl. dazu: Detlev Landgrebe: Kückallee 37. Eine Kindheit am Rande des Holocaust. Rheinbach 2009, ISBN 978-3-87062-104-9, S. 163, S. 167 u. a.</ref>
|-
| 15. Dezember 1945 || 31. Januar 1946 || Wilhelm Kleist
|-
| 1. Februar 1946 || 22. September 1946 || Carl Dobbertin
|-
| 23. September 1946 || 11. November 1948 || Alwin Hemken
|-
| 12. November 1948 || 28. April 1950 || Carl Dobbertin
|-
| 28. April 1950 || 31. März 1951 || Wilhelm Kleist
|-
| 1. April 1951 || 31. Dezember 1971 || [[Hermann Körner]]
|-
| 1. Januar 1972 || 31. Januar 1990 || Günther Kock
|-
| 1. Februar 1990 || 31. Januar 1996 || Manfred Neumann
|-
| 1. September 1996 || 31. August 2008 || Detlef Palm
|-
| 1. September 2008 || 31. August 2014 || [[Axel Bärendorf]]
|-
| 1. September 2014 || || [[Björn Warmer]]
|}
=== Wappen ===
[[Blasonierung]]: „In Rot ein silberner Wellenbalken, begleitet von drei im Dreipass mit den Stielen einander zugekehrten Eichenblättern, und zwar zwei oben und einem unten.“<ref>[{{SH-Wappenrolle|262|Stadt Reinbek, Kreis Stormarn|nurLink=1}} Kommunale Wappenrolle Schleswig-Holstein]</ref>
Die Blätter, in ihrer Anordnung an das Wappen der Familie [[Bismarck (Adelsgeschlecht)|Bismarck]] angelehnt, versteht man als Symbole für den [[Sachsenwald]], während das Band für die [[Bille]] steht. Eine ähnliche Symbolik findet sich auf den Wappen der Nachbarorte [[Wohltorf]] und [[Aumühle]]; die Farben Rot und Weiß entsprechen den Wappen [[Holstein]]s und [[Kreis Stormarn|Stormarns]]. Das Wappen wurde 1935 genehmigt.
=== Städtepartnerschaften ===
* 1956–2011: Städtefreundschaft mit [[Täby]] ([[Schweden]]). Der Marktplatz in Reinbek-Klosterbergen, der ''Täbyplatz'', wurde nach der Partnerstadt benannt.
* Seit 1961: Städtefreundschaft mit [[Königslutter am Elm]] ([[Niedersachsen]]).
* Seit 1974: Patenschaft zwischen der [[Freiwillige Feuerwehr|Freiwilligen Feuerwehr]] Ohe und der Gemeinde [[Padasjoki]] ([[Finnland]]).
* Seit 1999: Städtepartnerschaft mit [[Koło]] ([[Polen]]).
== Kultur und Sehenswürdigkeiten ==
[[Datei:Das Reinbeker Schloss.jpg|mini|Reinbeker Schloss]]
[[Datei:Museum Rade.JPG|mini|Ehemaliges Museum Rade]]
=== Theater, Kino und Museen ===
* Das Kultur- und Kongresszentrum ''Sachsenwald-Forum'' bietet ein wechselndes Programm von Tournee- und Privattheatern.
* Der Filmring Reinbek e. V. führt ehrenamtlich monatlich eine Kinoveranstaltung in der Nathan-Söderblom-Kirche durch.
* Das gegenüber vom Schloss gelegene ''Museum Rade'' stellte die Sammlung volkstümlicher Kunst des Hamburger Schriftstellers und Kunstsammlers [[Rolf Italiaander]] aus. Seit Sommer 2017 ist das Museum dauerhaft geschlossen, die Sammlung wurde Ende 2018 ins Schloss Reinbek verlegt
[[Datei:Reinbek Sankt Maria Magdalena.JPG|hochkant|mini|Maria-Magdalenen-Kirche]]
=== Bauwerke ===
[[Datei:Reinbek dänenbrücke P4070041.JPG|mini|„Dänenbrücke“ von 1793]]
[[Datei:RK 1810 P1650787 Bismarcksäule Friedrichsruh.jpg|mini|Bismarcksäule]]
Verschont von den Zerstörungswellen des Zweiten Weltkrieges, zeigt Reinbeks Stadtarchitektur ein kontinuierliches Bild durch die Epochen norddeutscher Baugeschichte, angefangen bei der niederländischen Renaissance und alten Bauernkaten, über [[großbürger]]liche Villen der Kaiserzeit, Klinkerexpressionismus der Weimarer Republik und Wohngroßbauten der 1970er bis hin zu einer eher behutsamen Architektur der 1990er Jahre.
* Ältestes und bedeutendstes Bauwerk ist das [[Schloss Reinbek]] im Stil der [[Niederländische Renaissance|Niederländischen Renaissance]]. [[Adolf I. (Schleswig-Holstein-Gottorf)|Herzog Adolf I. von Gottorf]] ließ das Schloss zwischen 1572 und 1576 in seiner heute noch vorhandenen Form errichten. Zunächst Nebenwohnsitz des Landesherren, war das Schloss in dänischer Zeit Residenz des Amtmannes und später kurzzeitig der Sitz des Landratsamtes für den [[Kreis Stormarn]]. Heute steht das originalgetreu restaurierte Gebäude für öffentliche Nutzung zur Verfügung.
* Über die 1793 erbaute ''Dänenbrücke'', in unmittelbarer Nähe zum Schloss, verlief einst der Verkehr zwischen dem dänischen Amt Reinbek und dem [[Herzogtum Sachsen-Lauenburg]].
* Die ''Schönningstedter Mühle'', erbaut 1886, wurde seit der Stilllegung (1968) als Gaststätte betrieben. Sie wurde durch einen Brand (1991) vollständig zerstört. Sie wurde durch eine andere am Ursprungsort abgebaute auf den Grundmauern der Alten Mühle neu errichtet.
* Die [[Bismarcksäule (Friedrichsruh)|Bismarcksäule]] auf dem ''Hammelsberg'' zwischen den Ortsteilen Krabbenkamp und Schönningstedt, in der Nähe des ehemaligen bismarckschen Guts Schönau, wurde 1903 fertiggestellt. Das 19 Meter hohe Monument entspricht dem üblichen Bismarcksäulen-Typus eines Feuerturmes, den [[Wilhelm Kreis]] 1898 entworfen hatte, und wurde aus Mitteln der deutschen Studentenschaft finanziert. Der Turm steht seit 1989 unter Denkmalschutz.
* In Reinbek gibt es [[Liste der Stolpersteine in Reinbek|sieben Stolpersteine]] zur Erinnerung an Opfer des [[Nationalsozialismus]].<ref>[http://www.akens.org/akens/texte/stolpersteine/Stolpersteineliste.htm#Reinbek Stolpersteine: Reinbek]</ref>
In der [[Liste der Kulturdenkmale in Reinbek]] stehen die in der Denkmalliste des Landes Schleswig-Holstein eingetragenen Kulturdenkmale.
=== Grünflächen und Naherholung ===
* Die Wald- und Wiesenlandschaft in und um Reinbek sowie der Schlosspark laden zum Spazieren, Wandern und Radfahren ein. Auf der Bille und auf dem Mühlenteich werden Kanufahrten veranstaltet.
* Jährlich wird in Reinbek auf dem Täbyplatz oder am ''Waldhaus'' im Sommer oder im Herbst die sogenannte „Reinbeker Sommersause“ bzw. „Reinbeker Herbstsause“ gefeiert. Bei diesen Festen treten unter anderem regionale Musiker und Coverbands auf.
=== Sport ===
* Das ''Freizeitbad Reinbek'' und der angrenzende ''Sport-Park Reinbek'' bieten neben einem Hallenbad mit Außenschwimmbecken auch eine Sauna und verschiedene Sportprogramme an.
* Die [[TSV Reinbek]] und der [[FC Voran Ohe]] bieten verschiedene Sportarten an.
== Wirtschaft, Infrastruktur, öffentliche Einrichtungen ==
=== Unternehmen ===
Reinbek zeichnet sich durch eine vielfältige, vorwiegend klein- und mittelständische Wirtschaftsstruktur aus.
Zahlreiche bedeutende Firmen hatten bzw. haben hier ihren Sitz, wie zum Beispiel der [[Rowohlt Verlag]] (von 1960 bis März 2019), E. Michaelis & Co. – Papiergroßhandel, [[Almirall]] Almirall Hermal und [[Dermapharm|Allergopharma]] (die seit Mai 2021 an der Herstellung des Impfstoffs von [[Biontech]] beteiligt sind)<ref>[https://www.ndr.de/nachrichten/schleswig-holstein/coronavirus/Corona-Biontech-Impfstoff-kommt-jetzt-auch-aus-Reinbek,spahn290.html ''Corona: Biontech-Impfstoff kommt jetzt auch aus Reinbek''] {{Webarchive|url=https://web.archive.org/web/20220328133934/https://www.ndr.de/nachrichten/schleswig-holstein/coronavirus/Corona-Biontech-Impfstoff-kommt-jetzt-auch-aus-Reinbek,spahn290.html |date=2022-03-28 }}, ndr.de, 30. April 2021</ref>, [[Fürst-Bismarck-Quelle]], Grossmann-Feinkost, [[Kahl Gruppe|Amandus Kahl]] (Neuhaus Neotec), Peek & Cloppenburg (Verteilzentrum) und Lutz Aufzüge (Maschinen- und Anlagentechnik), Wollenhaupt (Teehandel). Ein weiterer großer Arbeitgeber ist das Krankenhaus Reinbek St. Adolf-Stift (Gesundheitswesen).
Anfang der 1960er Jahre wurde das gemeinsame Gewerbegebiet Reinbek-[[Glinde]] erschlossen. Seitdem erfolgten immer wieder Erweiterungen und Neuausweisungen von Gewerbeflächen. Zuletzt wurde das Gewerbegebiet Haidland vermarktet (ca. 22 ha): bis 2018 sind dort mehr als 30 Firmen angesiedelt worden, dadurch wurden 1200 Arbeitsplätze gesichert und ca. 400 neu geschaffen. Geplant ist die Erweiterung des Gewerbegebietes.
Die wirtschaftliche Dynamik Reinbeks zeigt sich unter anderem in der Entwicklung der Gewerbebetriebe: deren Zahl stieg auf 2532 Betriebe (31. August 2018).
Auch die positiven Arbeitsmarktdaten sind ein Beweis für die Besonderheit des Standortes. Im Geschäftsstellenbezirk der Arbeitsagentur Bad Oldesloe wird der Bezirk Reinbek mit einer der niedrigsten Arbeitslosenquoten aufgeführt, vergleichbar mit denen süddeutscher Wirtschaftsregionen.
In der Region Südstormarn liegen einige der Kommunen mit der höchsten Kaufkraft in Deutschland. Auch Reinbek lag im Jahr 2017 mit einer Kaufkraftkennziffer von 118 über dem Durchschnitt (CIMA Lübeck, Jahresbericht interkommunales Einzelhandelsforum 2017).
Reinbek ist perspektivisch weiter ein dynamischer Wirtschaftsstandort mit einer hohen Gewerbeflächennachfrage und steigenden Gewerbesteuereinnahmen, u. a. wegen der verkehrsgünstigen zentralen Lage in der Metropolregion direkt benachbart der Weltstadt Hamburg. Die Arbeitsplatzzentralität ist mit einem knapp 80-%-Anteil an den Beschäftigten hoch.
=== Öffentliche Einrichtungen ===
Reinbek ist Sitz eines [[Amtsgericht]]s.
=== Bildung ===
In Reinbek gibt es vier [[Grundschule]]n, eine [[Gemeinschaftsschule mit Oberstufe]] (mit auslaufenden Haupt- und Realschulklassen) und ein [[Sachsenwaldschule Gymnasium Reinbek|Gymnasium]]. Außerdem gibt es eine [[Förderschule (Deutschland)|Förderschule]].
Gemeinschaftsschule und Förderschule sind zum Schulzentrum Mühlenredder zusammengefasst.
Die ''Volkshochschule Sachsenwald'' hat ein umfangreiches Angebot an Kursen verschiedener Fachrichtungen und deckt auch das Angebot für die Nachbargemeinde [[Wentorf bei Hamburg|Wentorf]] mit ab. Die meisten Kurse finden im eigenen, gut ausgestatteten Haus mitten in Reinbek statt.
Die ''Reinbeker Stadtbibliothek'' bietet ein breit gefächertes Angebot aus alten wie neuen Medien und unterhält einen ständigen Bücherflohmarkt aus gespendeten und ausgemusterten Büchern.
Seit 1989 besteht der [[Museumsverein Reinbek|Geschichts- und Museumsverein Reinbek e. V.]]
=== Verkehr ===
[[Datei:Reinbeker Bahnhof.jpg|mini|Der Reinbeker Bahnhof]]
Reinbek liegt in der [[Metropolregion Hamburg]]. Von Reinbek ist die Hamburger Innenstadt mit der [[S-Bahn Hamburg|S-Bahn-Linie]] S 21 in 25 Minuten zu erreichen. Die S-Bahn verbindet Reinbek mit den Nachbarorten [[Wohltorf]] und [[Bahnhof Aumühle|Aumühle]], innerhalb Reinbeks fahren mehrere Buslinien, die von den zum [[Hamburger Verkehrsverbund|HVV]] gehörenden [[VHH PVG Unternehmensgruppe|VHH]] betrieben werden.
Die Fernverkehrsstraßen [[Bundesstraße 5|B 5]], [[Bundesautobahn 24|A 24]] und [[Bundesautobahn 1|A 1]] führen in die Hamburger Innenstadt bzw. in Richtung [[Berlin]], [[Lübeck]] und [[Bremen]].
Der nächstgelegene Fernbahnhof ist [[Bahnhof Hamburg-Bergedorf|Hamburg-Bergedorf]], die [[Bahnstrecke Hamburg–Berlin]] durchquert die Stadt ohne Halt parallel zur S-Bahn.
== Persönlichkeiten ==
=== Ehrenbürger ===
<!-- chronologisch nach Geburtsdatum geordnet -->
{{Mehrspaltige Liste|liste=
* [[Paul Lingens]] (1895–1976), Stadtverordneter der CDU, Bürgervorsteher
* Karl Meißner (1912–2010), Stadtverordneter der SPD, Bürgervorsteher
* [[Georges-Arthur Goldschmidt]] (* 1928), französisch-deutscher Schriftsteller, Essayist und Übersetzer
* Lothar Zug (1928–2020), Stadtverordneter der CDU, Bürgervorsteher
* Helmut Schomann (1932–2009), Stadtverordneter der SPD, Bürgervorsteher
}}
=== Söhne und Töchter der Stadt ===
<!-- chronologisch nach Geburtsdatum geordnet -->
{{Mehrspaltige Liste|liste=
* [[Minna Specht]] (1879–1961), Pädagogin und Sozialistin
* [[Wilhelm Bisse]] (1881–1946), Reichstagsabgeordneter der NSDAP
* [[Horst Seifart]] (1916–2004), Journalist und Fernseh-Regisseur
* [[Donat de Chapeaurouge]] (1925–2019), Kunsthistoriker
* [[Georges-Arthur Goldschmidt]] (* 1928), französisch-deutscher Schriftsteller, Essayist und Übersetzer
* Helmut Schomann (1932–2009), Politiker, Ehrenbürger und Träger des Bundesverdienstkreuzes
* [[Hartmut Berg]] (* 1936), Wirtschaftswissenschaftler
* [[Ekkehard Wachmann]] (* 1937), Entomologe
* [[Wittko Francke]] (1940–2020), Chemiker
* [[Hans Klapdor-Kleingrothaus]] (* 1942), Physiker
* [[Klaus-Peter Puls]] (* 1943), Politiker
* [[Albert Maringer]] (* 1945), Manager
* [[Wolfgang Seifert (Japanologe)|Wolfgang Seifert]] (* 1946), Japanologe
* [[Claus Peter Ortlieb]] (1947–2019), Mathematiker
* [[Eckart Modrow]] (* 1948), Pädagoge und Sachbuchautor
* [[Johannes Spallek]] (* 1948), Archivar und Kulturreferent
* [[Christine Christ-von Wedel]] (* 1948), Historikerin
* [[Dieter Matz]] (* 1948), Sportjournalist
* [[Angela Sommer-Bodenburg]] (* 1948), Kinderbuchautorin und Malerin; bekannt wurde sie durch ihre Bücher über den ''Kleinen Vampir''
* [[Christel Hüttemann]] (* 1949), Trägerin des Bundesverdienstkreuzes
* [[Mathias Nolte]] (* 1952), Buchautor und Journalist
* [[Mathias Petersen]] (* 1955), Politiker
* [[Harald Lemke (Politiker, 1956)|Harald Lemke]] (* 1956), Staatssekretär
* [[Norbert Meier]] (* 1958), Fußballtrainer und ehemaliger -spieler
* [[Sabine Sütterlin-Waack]] (* 1958), Rechtsanwältin und Politikerin (CDU)
* [[Martin Rheinheimer]] (* 1960), Historiker
* [[Jan van Aken (Politiker)|Jan van Aken]] (* 1961), Politiker
* [[Dietrich Becker (Diplomat)|Dietrich Becker]] (* 1961), Diplomat
* [[Ralf Sommer (Elektroingenieur)|Ralf Sommer]] (* 1961), Elektroingenieur
* [[Thomas Röske]] (* 1962), Kunsthistoriker
* [[Gerd Gottlob]] (* 1964), Journalist und Fußballkommentator
* [[Gundula Bavendamm]] (* 1965), Historikerin und Kulturmanagerin
* [[Christiane Bruns (Medizinerin)|Christiane Bruns]] (* 1965), Chirurgin
* [[Kerstin Drechsel]] (* 1966), Malerin
* [[Andreas Herbig]] (1966–2022), Produzent, Echopreisträger
* [[Birte Karalus]] (* 1966), Journalistin und Moderatorin
* [[Lena Johannson]] (* 1967), Schriftstellerin
* [[Michael Meyer-Hermann (Physiker)|Michael Meyer-Hermann]] (* 1967), Physiker und Hochschullehrer
* [[Thorsten Schröder]] (* 1967), Journalist, Moderator und Sprecher der Tagesschau
* [[Lars Uwe Höltich]] (* 1968), TV-Producer
* [[Sönke Lieberam-Schmidt]] (* 1969), Wirtschaftsinformatiker und Professor
* [[Heiko Nieder]] (* 1972), Koch, mit zwei Sternen im ''Guide Michelin'' ausgezeichnet
* [[Christine Berger]] (* 1973), Theater- und Fernsehschauspielerin
* [[Andreas Dobberkau]] (* 1975), Schauspieler
* [[Helmut Fritz]] (* 1975), fiktiver Popsänger
* [[Julian Krafftzig]] (* 1977), Radiomoderator
* [[Torben Liebrecht]] (* 1977), Schauspieler, Regisseur und Drehbuchautor
* [[Alexander Nerlich]] (* 1979), Regisseur
* [[Imke Wedekind]] (* 1984), Volleyballspielerin
* [[Ann-Kathrin Karschnick]] (* 1985), Fantasy-Autorin
* [[Max Kruse (Fußballspieler)|Max Kruse]] (* 1988), Fußballspieler
* [[Marvin Boadu]] (* 1989), Basketballspieler
* [[Felix Brügmann]] (* 1992), Fußballspieler
* [[Maximilian Buhk]] (* 1992), Automobilrennfahrer
* [[Felix von der Laden]] (* 1994), Webvideoproduzent (bekannt als „Dner“), Automobilrennfahrer und Unternehmer
* [[Sina Aylin Demirhan]] (* 1994), Politikerin (Bündnis 90/Die Grünen)
* [[Larina Aylin Hillemann]] (* 1996), Ruderin
* [[Victoria Helene Bergemann]] (* 1997), Komikerin und Autorin
* [[Noma Noha Akugue]] (* 2003), Tennisspielerin
}}
=== Mit Reinbek verbunden ===<!-- chronologisch nach Geburtsdatum geordnet -->
{{Mehrspaltige Liste|liste=
* [[Georg Julius Andresen]] (1815–1882), Autor, Mediziner, Hydrotherapeut und Gründer des Sophienbads
* [[Arthur Goldschmidt (Jurist)|Arthur Goldschmidt]] (1873–1947), Jurist und Politiker
* [[Hans E. B. Kruse]] (1891–1968), Kaufmann und Hamburger Senator, wohnte und starb in Reinbek
* [[Franz Heske]] (1892–1963), Forstwissenschaftler
* [[Bernhard Rogge (Marineoffizier)|Bernhard Rogge]] (1899–1982), Admiral
* [[Helene Francke-Grosmann]] (1900–1990), Forstwissenschaftlerin
* [[Erwin Freytag]] (1907–1987), Autor und evangelisch-lutherischer Theologe
* [[Heinrich Maria Ledig-Rowohlt]] (1908–1992), bis 1982 Verleger des [[Rowohlt Verlag]]s
* [[Rolf Italiaander]] (1913–1991), Schriftsteller, Übersetzer, Forschungsreisender, Ethnograf
* [[Sandro von Lorsch]] (1919–1992), Maler
* [[Arwed Imiela]] (1929–1982), Frauenmörder
* [[Günter Gaus]] (1929–2004), Journalist, Publizist, Diplomat und Politiker
* [[Hans-Jürgen von Maydell]] (''Baron Maydell''; 1932–2010), Forstwissenschaftler
* [[Heinz-Georg Keerl]] (1946–2011), General
* [[Thomas Straubhaar]] (* 1957), Ökonom
* [[Holger Waldenberger]] (* 1967), Quizspieler
* [[Bjarne Mädel]] (* 1968), Schauspieler
* [[Moritz Bleibtreu]] (* 1971), Schauspieler
* [[Ann-Katrin Schröder]] (* 1973), Journalistin und Fernsehmoderatorin
* [[Bodo Wartke]] (* 1977), Musik-Kabarettist
* [[Martin Habersaat]] (* 1977), Politiker, lebt seit 2014 in Reinbek
* [[Julian Reister]] (* 1986), Tennisspieler
}}
== Literatur ==
;Antiquarisch
* Mathilde Weise-Minck: ''Kindertage in Reinbek.'' Piper, München 1947, {{DNB|576902853}}.
* Curt Davids: ''Festschrift zur 725-Jahrfeier von Reinbek.'' 1963, {{DNB|451252543}}.
* Walter Fink: ''Das Amt Reinbek.'' Zentralstelle f. Personen- u. Familiengeschichte, Frankfurt am Main 1969, {{DNB|999410660}}.
* Herbert Rathmann: ''Ich bin ein Reinbeker.'' 1978, {{OCLC|248265316}}.
* Curt Davids: ''Die Wassermühle in Reinbek.'' 1982, {{DNB|840196717}}.
* Hans Heuer: ''Das Kloster Reinbek.'' Beitrag zur Geschichte der Landschaft Stormarn. Wachholtz, Neumünster 1985, ISBN 3-529-02186-5.
* [[Dirk Bavendamm]]: ''Reinbek. Geschichte einer holsteinischen Stadt zwischen Hamburg und Sachsenwald.'' 1988, ISBN 3-9801817-0-7.
* ''Reinbek in alten Ansichten.'' Bildband. Europäische Bibliothek, Zaltbommel 1996, ISBN 90-288-6082-7.
;Aktuellere Titel
* Wolf Gütschow, Michael Zapf: ''Reinbek und der Sachsenwald im Wandel.'' Bildband. Schubert, Hamburg 1997, ISBN 3-929229-44-7.
* ''Reinbek gestern und heute.'' Bildband. Europäische Bibliothek, Zaltbommel 2000, ISBN 90-288-6634-5.
* Georges-Arthur Goldschmidt: ''Ein Garten in Deutschland.'' 2000, ISBN 3-250-10118-4.
* Frank Göhre: ''Endstation Reinbek.'' Krimi. Hamburger Abendblatt, Hamburg 2001, ISBN 3-921305-20-9.
* Antje Wendt: ''Das Schloß Reinbek.'' Wachholtz, Neumünster 1994, ISBN 3-529-02739-1.
* Detlev Landgrebe: ''Kückallee 37: Eine Kindheit am Rande des Holocaust.'' CMZ, Rheinbach 2009, ISBN 978-3-87062-104-9.
== Weblinks ==
{{Commonscat}}
{{Wikivoyage}}
* [https://www.reinbek.de/ Website der Stadt Reinbek]
== Einzelnachweise ==
<references />
{{NaviBlock
|Navigationsleiste Städte und Gemeinden im Kreis Stormarn
|Navigationsleiste Stadtteile von Reinbek}}
{{Normdaten|TYP=g|GND=4049222-9|LCCN=n50052582|VIAF=312788140}}
[[Kategorie:Ort im Kreis Stormarn]]
[[Kategorie:Ort an der Bille]]
[[Kategorie:Reinbek| ]]
[[Kategorie:Ersterwähnung 1238]]
[[Kategorie:Stadt in Schleswig-Holstein]]
[[Kategorie:Stadtrechtsverleihung 1952]]
662mj3g32r6t7m0uw1b3ydmnt611kvz
Test:RandomGeoTest3477
0
166053
739040
657022
2026-04-21T20:10:56Z
WBrown (WMF)
58818
Test
739040
wikitext
text/x-wiki
Potato.Test
0ms27azt44xztn20p38xcdkfjz7loa8
User:Iniquity/edithere-core.js
2
167481
739043
669875
2026-04-21T22:48:17Z
MusikAnimal
24817
CodeMirror v6 modules have been moved ([[phab:T373720]])
739043
javascript
text/javascript
// Quick Edit gadget
// Edit sections of a page without leaving the article
// Based on [[:en:w:User:BrandonXLF/QuickEdit]] by [[:en:w:User:BrandonXLF]]
// Some changes from [[meta:User:Jack who built the house/editHere.js]] by [[meta:User:Jack who built the house]]
// Extra functions and refactoring by [[meta:Iniquity]]
(function () {
let mobile = mw.config.get('skin') === 'minerva';
let titleRegexp = new RegExp(
mw.config.get('wgArticlePath').replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\$1/, '([^?]+)') +
'|[?&]title=([^&#]*)'
);
let apiSingleton;
function api(func, params) {
if (!apiSingleton) {
apiSingleton = new mw.Api();
}
$.extend(params, {
errorformat: 'html',
errorlang: mw.config.get('wgUserLanguage'),
errorsuselocal: true
});
return apiSingleton[func](params).fail(function (_, data) {
mw.notify(apiSingleton.getErrorMessage(data), {
type: 'error',
tag: 'qe'
});
});
}
function getPageInfo(title, sectionID) {
return api('get', {
action: 'query',
curtimestamp: 1,
prop: 'revisions',
indexpageids: 1,
titles: title,
rvprop: ['timestamp', 'content'],
rvslots: 'main',
rvsection: sectionID
}).then(function (res) {
let rev = res.query.pages[res.query.pageids[0]].revisions[0];
return {
start: res.curtimestamp,
base: rev.timestamp,
full: rev.slots.main['*']
};
});
}
function getPreviewCallback(editor) {
editor.children('.preview').remove();
// -- Minimal, library-agnostic preview progress indicator
$('<div>')
.addClass('qe-progress-bar')
.text('Loading preview…')
.appendTo(editor);
return function (html) {
editor.children('.preview').remove();
editor.children('.qe-progress-bar').remove();
const $preview = $('<div>')
.html(html)
.addClass('qe-border-color-base qe-preview-block')
.addClass('preview')
.appendTo(editor);
mw.hook('wikipage.content').fire($preview);
};
}
function showCompare(editor, title, from, to) {
mw.loader.load('mediawiki.diff.styles');
api('post', {
action: 'compare',
fromslots: 'main',
'fromtext-main': from,
fromtitle: title,
frompst: 'true',
toslots: 'main',
'totext-main': to,
totitle: title,
topst: 'true'
}).then(function (r) {
return r.compare['*'] ? $('<table>').addClass('diff').append(
$('<colgroup>').append(
$('<col>').addClass('diff-marker'),
$('<col>').addClass('diff-content'),
$('<col>').addClass('diff-marker'),
$('<col>').addClass('diff-content')
)
).append(r.compare['*']) : mw.msg('diff-empty');
}).then(getPreviewCallback(editor));
}
// Parts taken from EditPage::extractSectionTitle and Parser::stripSectionName
function getSectionSummary(text) {
let match = text.match(/^(=+)(.+)\1\s*(\n|$)/);
return !match ? '' : '/* ' + match[2].trim()
// Strip internal link markup
.replace(/\[\[:?([^[|]+)\|([^[]+)\]\]/g, '$2')
.replace(/\[\[:?([^[]+)\|?\]\]/g, '$1')
// Strip external link markup
.replace(new RegExp('\\[(?:' + mw.config.get('wgUrlProtocols') + ')([^ ]+?) ([^\\[]+)\\]', 'ig'), '$2')
// Remove wikitext quotes
.replace(/(''|'''|''''')(?!')/g, '')
// Strip HTML tags
.replace(/<[^>]+?>/g, '') + ' */ ';
}
async function showEditor($element) {
let progress = $('<div>').addClass('qe-progress-bar').text('Loading…');
// https://www.mediawiki.org/wiki/Heading_HTML_changes
// Cannot use .closest() because DiscussionTools nests an h2 within a .mw-heading
let heading = $element.parents(':header, .mw-heading').last();
let matcher = heading.nextUntil.bind(heading);
let inserter = heading.after.bind(heading);
let targetEl = $element.siblings('.qe-target').last();
let titleMatch = targetEl.attr('href').match(titleRegexp);
let title = decodeURIComponent(titleMatch[1] || titleMatch[2]);
let sectionID = /[?&]v?e?section=T?-?(\d*)/.exec(targetEl.attr('href'))[1];
if (!heading.closest('.mw-parser-output').length) {
let $articleContent = $('#mw-content-text .mw-parser-output');
matcher = function (selector) {
let $child = $articleContent.children(selector).first();
if ($child.length) {
return $child.prevAll();
}
return $articleContent.children();
};
inserter = $articleContent.prepend.bind($articleContent);
}
inserter(progress);
$element.addClass('qe-loading');
$('.qe-hide').removeClass('qe-hide');
$('.qe-heading').removeClass('qe-heading');
$('#qe-editor').remove();
getPageInfo(title, sectionID).then(async function (r) {
await (new mw.Api()).loadMessages([
'publishchanges',
'tooltip-publish',
'accesskey-publish',
'showpreview',
'tooltip-preview',
'accesskey-preview',
'showdiff',
'tooltip-diff',
'accesskey-diff',
'cancel',
'word-separator',
'brackets',
'diff-empty',
'summary',
'minoredit',
'summary-preview'
]);
var start = r.start,
base = r.base,
full = r.full,
saving = false,
expanded = false,
remainderStart = full.match(/\n=+.+=+(?:\n|$)/),
part = remainderStart ? full.substring(0, remainderStart.index) : full,
remainder = remainderStart ? full.substring(remainderStart.index) : '',
level = 0,
editor;
full.replace(/^(=+).+?(=+)(?:\n|$)/, function (m, a, b) {
level = Math.min(a.length, b.length);
return m;
});
var levelMatch = 'h1';
for (var i = 2; i <= level; i++)
levelMatch += ', h' + i + ':has(*), .mw-heading' + i;
var partSection = matcher(':header:has(*), .mw-heading'),
fullSection = matcher(levelMatch),
// -- Native textarea to keep WikiEditor/CodeMirror compatibility
$textarea = $('<textarea>')
.addClass('qe-textarea')
.val(part),
// -- Summary input
$summaryInput = $('<input>')
.attr('type', 'text')
.addClass('qe-summary-input')
.val(getSectionSummary(part));
// -- Shim objects for existing integrations expecting OOUI-like shape
var textareaShim = { $input: $textarea, $element: $textarea };
var summaryShim = { $input: $summaryInput, $element: $summaryInput };
// -- Will render Codex buttons later via Vue
partSection.addClass('qe-hide');
heading.addClass('qe-heading');
$element.removeClass('qe-loading');
progress.remove();
$textarea.addClass('qe-textarea');
$summaryInput.on('keydown', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
if (typeof window.__qeCodexActions !== 'undefined' && window.__qeCodexActions.onSave) {
window.__qeCodexActions.onSave();
}
}
});
function handleSave() {
if (saving) return;
var fullText = $textarea.val() + (expanded ? '' : remainder);
saving = true;
api('postWithEditToken', {
action: 'edit',
title: title,
section: sectionID,
summary: $summaryInput.val(),
text: fullText,
minor: window.__qeCodexState && window.__qeCodexState.minor ? true : undefined,
notminor: window.__qeCodexState && window.__qeCodexState.minor ? undefined : true,
starttimestamp: start,
basetimestamp: base
}).then(function () {
api('get', {
action: 'parse',
page: mw.config.get('wgPageName'),
prop: ['text', 'categorieshtml']
}).then(function (r) {
var contentText = $('#mw-content-text'),
catLinks = $('#catlinks');
contentText.find('.mw-parser-output').replaceWith(r.parse.text['*']);
mw.hook('wikipage.content').fire(contentText);
catLinks.replaceWith(r.parse.categorieshtml['*']);
mw.hook('wikipage.categories').fire(catLinks);
// -- Clean up WikiEditor and CodeMirror after successful save
if (typeof $textarea.wikiEditor === 'function') {
try {
var $cmWrapper = $textarea.closest('.ext-codemirror-wrapper');
if ($cmWrapper.length) {
var cmInstance = $cmWrapper.data('cmInstance');
if (cmInstance && typeof cmInstance.destroy === 'function') {
cmInstance.destroy();
}
}
$textarea.wikiEditor('destroy');
} catch (e) {
console.error('Error destroying WikiEditor/CodeMirror after save:', e);
}
}
// -- Remove editor and restore page layout after successful save
if (window.__qeCodexApp && window.__qeCodexApp.unmount) {
try { window.__qeCodexApp.unmount(); } catch (e) {}
window.__qeCodexApp = null;
}
editor.remove();
heading.removeClass('qe-heading');
fullSection.removeClass('qe-hide');
// -- Signal the editor is closing
if (window.qeIntegrations && typeof window.qeIntegrations.onEditorClose === 'function') {
window.qeIntegrations.onEditorClose();
}
saving = false;
});
}, function (code) {
if (code == 'editconflict') {
showEditConflict(editor, title, sectionID, fullText).then(function (r) {
start = r.start;
base = r.base;
// -- r.textarea is a jQuery element now
$textarea = r.textarea;
expanded = true;
});
}
saving = false;
});
}
function handlePreview() {
let summaryValue = $summaryInput.val();
// Split summary into section and rest text
let sectionMatch = summaryValue.match(/^\/\*\s*([^*]+?)\s*\*\/(.*)$/);
let summaryHtml = '';
if (sectionMatch) {
const section = sectionMatch[1].trim();
const rest = sectionMatch[2] ? sectionMatch[2].trimStart() : '';
const anchor = '#' + section.replace(/ /g, '_');
// Generate HTML for link to section with comment and arrow
summaryHtml = '<span class="comment"><span class="autocomment">→<a href="' + anchor + '"><bdi dir="ltr">' + section + '</bdi></a>: </span>';
// Parse only rest text
const restParsePromise = api('post', {
action: 'parse',
title: title,
prop: 'text',
pst: 'true',
disablelimitreport: 'true',
disableeditsection: 'true',
sectionpreview: 'true',
disabletoc: 'true',
text: rest
});
const mainPreviewPromise = api('post', {
action: 'parse',
title: title,
prop: 'text',
pst: 'true',
disablelimitreport: 'true',
disableeditsection: 'true',
sectionpreview: 'true',
disabletoc: 'true',
text: $textarea.val()
});
Promise.all([restParsePromise, mainPreviewPromise]).then(function ([restRes, mainRes]) {
// Remove outer <div> using jQuery or regex
let restHtml = restRes.parse.text['*'];
if (restHtml.startsWith('<div')) {
// jQuery preferred if available
try {
restHtml = $(restHtml).html();
} catch (e) {
restHtml = restHtml.replace(/^<div[^>]*>[\s\n\r]*<p>/, '<div>').replace(/<\/p>[\s\n\r]*<\/div>$/, '</div>');
}
}
// Remove only outer <p>...</p> if it wraps the entire result
restHtml = restHtml.replace(/^<p>([\s\S]*)<\/p>$/i, '$1');
const summaryPreviewHtml =
'<div class="qe-summary-preview qe-border-color-base">' +
'<b>' + mw.msg('summary-preview') + '</b><br>' +
summaryHtml + restHtml + '</span>' +
'</div>';
const mainPreviewHtml = mainRes.parse.text['*'] + '<div style="clear:both;"></div>';
return summaryPreviewHtml + mainPreviewHtml;
}).then(getPreviewCallback(editor));
} else {
// No pattern /* ... */ — parse as wikitext
const summaryParsePromise = api('post', {
action: 'parse',
title: title,
prop: 'text',
pst: 'true',
disablelimitreport: 'true',
disableeditsection: 'true',
sectionpreview: 'true',
disabletoc: 'true',
text: summaryValue
});
const mainPreviewPromise = api('post', {
action: 'parse',
title: title,
prop: 'text',
pst: 'true',
disablelimitreport: 'true',
disableeditsection: 'true',
sectionpreview: 'true',
disabletoc: 'true',
text: $textarea.val()
});
Promise.all([summaryParsePromise, mainPreviewPromise]).then(function ([summaryRes, mainRes]) {
const summaryPreviewHtml =
'<div class="qe-summary-preview qe-border-color-base">' +
'<b>' + mw.msg('summary-preview') + '</b><br>' +
'<span class="comment">' + summaryRes.parse.text['*'] + '</span>' +
'</div>';
const mainPreviewHtml = mainRes.parse.text['*'] + '<div style="clear:both;"></div>';
return summaryPreviewHtml + mainPreviewHtml;
}).then(getPreviewCallback(editor));
}
}
function handleCompare() {
showCompare(editor, title, part + (expanded ? remainder : ''), $textarea.val());
}
function handleCancel() {
// -- Clean up WikiEditor and CodeMirror before removing the editor
if (typeof $textarea.wikiEditor === 'function') {
try {
var $cmWrapper = $textarea.closest('.ext-codemirror-wrapper');
if ($cmWrapper.length) {
var cmInstance = $cmWrapper.data('cmInstance');
if (cmInstance && typeof cmInstance.destroy === 'function') {
cmInstance.destroy();
}
}
$textarea.wikiEditor('destroy');
} catch (e) {
console.error('Error destroying WikiEditor/CodeMirror:', e);
}
}
if (window.__qeCodexApp && window.__qeCodexApp.unmount) {
try { window.__qeCodexApp.unmount(); } catch (e) {}
window.__qeCodexApp = null;
}
editor.remove();
heading.removeClass('qe-heading');
fullSection.removeClass('qe-hide');
if (window.qeIntegrations && typeof window.qeIntegrations.onEditorClose === 'function') {
window.qeIntegrations.onEditorClose();
}
}
function handleMore() {
expanded = true;
$textarea.val($textarea.val() + remainder);
// Step-by-step height debugging: find the first .ext-codemirror-wrapper on the page
var $wrapper = $('.ext-codemirror-wrapper').first();
setTimeout(function() {
var $textarea = $wrapper.find('textarea');
if ($textarea.length) {
var height = $textarea.outerHeight();
var $nextDiv = $textarea.next('div');
if ($nextDiv.length) {
$nextDiv.height(height);
$nextDiv.css('height', height + 'px');
$nextDiv[0].style.setProperty('height', height + 'px', 'important');
}
}
}, 5);
fullSection.addClass('qe-hide');
}
editor = $('<div id="qe-editor">').append(
$('<div>').addClass('qe-editor-block qe-background-color-interactive qe-color-base qe-border-color-base').append(
$textarea,
$('<div>').addClass('qe-edit-options').append(
$('<div id="wpSummaryWidget">').addClass('qe-border-color-base').append(
$('<div>').html(mw.message('summary').parse()),
$summaryInput
),
$('<div id="qe-codex-buttons">').addClass('qe-buttons qe-border-color-base'),
title !== mw.config.get('wgPageName') ? $('<div>').addClass('qe-not-current-page qe-border-color-base').append(
'Editing page: ',
$('<a>').attr('href', mw.config.get('wgArticlePath').replace('$1', title)).addClass('qe-not-current-page-link').text(title.replace(/_/g, ' '))
) : undefined
)
)
);
inserter(editor);
// Initialize WikiEditor with CodeMirror for the native textarea
mw.loader.using([
'ext.wikiEditor',
'ext.CodeMirror.WikiEditor',
'ext.CodeMirror.mode.mediawiki'
]).then(function(require) {
if (typeof mw.addWikiEditor === 'function') {
try {
mw.addWikiEditor($textarea);
$textarea.wikiEditor({
toolbar: {
format: {},
insert: {},
advanced: {}
}
});
const CodeMirrorWikiEditor = require('ext.CodeMirror.WikiEditor');
const mediawikiLang = require('ext.CodeMirror.mode.mediawiki');
const cmWe = new CodeMirrorWikiEditor($textarea[0], mediawikiLang());
cmWe.mode = 'mediawiki'; // Явно указываем mode для совместимости с CodeMirrorWikiEditor
cmWe.initialize();
} catch (e) {
console.error('[qe] Error initializing WikiEditor with CodeMirror:', e);
}
}
// Force integration call via setTimeout
setTimeout(function() {
if (window.qeIntegrations && typeof window.qeIntegrations.applyExternalIntegrations === 'function') {
window.qeIntegrations.applyExternalIntegrations(textareaShim, summaryShim);
} else {
console.warn('[qe] qeIntegrations or applyExternalIntegrations not found');
}
}, 50);
}).catch(function(error) {
console.error('[qe] Failed to load WikiEditor, CodeMirror, or Wikificator:', error);
});
// Integration of external tools via hook
$textarea.on('wikiEditor-toolbar-done', function () {
if (window.qeIntegrations && typeof window.qeIntegrations.applyExternalIntegrations === 'function') {
window.qeIntegrations.applyExternalIntegrations(textareaShim, summaryShim);
} else {
console.warn('[qe] qeIntegrations or applyExternalIntegrations not found');
}
});
// Fix cases when editing a section next to two floating images like at
// https://en.wikipedia.org/w/index.php?title=Elevation&oldid=1235703526#Aviation
// -- No updatePosition for native textarea
// -- Render Codex controls (load full Codex bundle for gadgets/userscripts)
mw.loader.using('@wikimedia/codex').then(function (require) {
const Vue = require('vue');
const CodexLib = require('@wikimedia/codex');
const { CdxButton, CdxCheckbox, CdxButtonGroup, CdxProgressBar } = CodexLib || {};
try { console.log('[qe] Codex exports:', Object.keys(CodexLib || {})); } catch (e) {}
var app = Vue.createMwApp({
data: function () {
return {
isSaving: false,
canMore: part !== full && !expanded,
minor: false,
labels: {
minoredit: mw.msg('minoredit'),
publish: mw.msg('publishchanges'),
preview: mw.msg('showpreview'),
diff: mw.msg('showdiff'),
cancel: mw.msg('cancel')
}
};
},
methods: {
onSave: function () {
if (this.isSaving) return;
this.isSaving = true;
window.__qeCodexState.minor = this.minor;
handleSave();
// Saving state will be reset after parse
setTimeout(() => { this.isSaving = false; }, 1000);
},
onPreview: function () { handlePreview(); },
onCompare: function () { handleCompare(); },
onCancel: function () { handleCancel(); },
onMore: function () {
handleMore();
this.canMore = false;
}
},
template: `
<div class="qe-buttons qe-border-color-base">
<div style="margin-bottom:6px"><cdx-checkbox v-model="minor">{{ labels.minoredit }}</cdx-checkbox></div>
<div class="qe-cdx-buttons-row" style="display:flex;gap:6px;flex-wrap:wrap;align-items:center;">
<cdx-button :disabled="isSaving" action="progressive" weight="primary" @click="onSave">{{ labels.publish }}</cdx-button>
<cdx-button :disabled="isSaving" @click="onPreview">{{ labels.preview }}</cdx-button>
<cdx-button :disabled="isSaving" @click="onCompare">{{ labels.diff }}</cdx-button>
<cdx-button :disabled="isSaving" weight="quiet" action="destructive" @click="onCancel">{{ labels.cancel }}</cdx-button>
<cdx-button v-if="canMore" :disabled="isSaving" @click="onMore" title="Edit the entire section (including subsections)">+</cdx-button>
</div>
<cdx-progress-bar v-if="isSaving" class="qe-progress-bar" />
</div>
`
});
// Register Codex plugin and components explicitly for reliability
try { app.use(CodexLib); } catch (e) {}
try {
if (CdxButton) {
app.component('CdxButton', CdxButton);
app.component('cdx-button', CdxButton);
}
if (CdxCheckbox) {
app.component('CdxCheckbox', CdxCheckbox);
app.component('cdx-checkbox', CdxCheckbox);
}
if (CdxButtonGroup) {
app.component('CdxButtonGroup', CdxButtonGroup);
app.component('cdx-button-group', CdxButtonGroup);
}
if (CdxProgressBar) {
app.component('CdxProgressBar', CdxProgressBar);
app.component('cdx-progress-bar', CdxProgressBar);
}
} catch (e) {}
window.__qeCodexState = { minor: false };
window.__qeCodexActions = {
onSave: function () {
try {
if (window.__qeCodexApp && window.__qeCodexApp._instance && window.__qeCodexApp._instance.proxy) {
window.__qeCodexApp._instance.proxy.onSave();
} else {
handleSave();
}
} catch (e) { handleSave(); }
}
};
window.__qeCodexApp = app;
app.mount('#qe-codex-buttons');
// Fallback: if buttons are missing or invisible, append HTML buttons (do not remove checkbox)
setTimeout(function () {
var host = document.getElementById('qe-codex-buttons');
if (!host) return;
var codexBtn = host.querySelector('.cdx-button');
var visible = codexBtn && codexBtn.offsetWidth > 0 && codexBtn.offsetHeight > 0 && getComputedStyle(codexBtn).visibility !== 'hidden';
if (!visible) {
console.warn('[qe] Codex buttons not detected or invisible, rendering HTML fallback');
function makeBtn(text, onClick, extraStyle) {
var b = document.createElement('button');
b.setAttribute('data-qe-fallback', '');
b.type = 'button';
b.textContent = text;
b.style.marginRight = '6px';
if (extraStyle) Object.assign(b.style, extraStyle);
b.addEventListener('click', onClick);
return b;
}
var bar = document.createElement('div');
bar.style.marginTop = '6px';
bar.appendChild(makeBtn(mw.msg('publishchanges'), handleSave, { fontWeight: '600' }));
bar.appendChild(makeBtn(mw.msg('showpreview'), handlePreview));
bar.appendChild(makeBtn(mw.msg('showdiff'), handleCompare));
bar.appendChild(makeBtn(mw.msg('cancel'), handleCancel));
if (part !== full && !expanded) {
bar.appendChild(makeBtn('+', function () { handleMore(); }));
}
host.appendChild(bar);
}
}, 250);
});
}, function () {
$element.removeClass('qe-loading');
progress.remove();
});
}
// FIXIT: It is necessary to rework it to call the standard MediaWiki interface, it can also be done in an iframe
function showEditConflict(editor, title, sectionID, text) {
return getPageInfo(title, sectionID).then(function (r) {
var $left = $('<textarea>')
.addClass('qe-textarea')
.val(r.full),
$right = $('<textarea>')
.addClass('qe-textarea')
.val(text);
function syncSize() {
var height = Math.max($left.outerHeight(), $right.outerHeight());
$left.height(height);
$right.height(height);
}
editor.find('> :first-child > :first-child').remove();
$('<table>').addClass('qe-border-color-base').css({
width: '100%',
borderWidth: '1px',
borderStyle: 'solid',
borderBottom: 'none',
borderSpacing: '0',
margin: '0 !important'
}).append(
$('<tr>').append(
$('<th>').addClass('qe-table-th')
.text('Their version (to be saved)'),
$('<th>').css({
width: '50%',
paddingTop: '4px'
}).text('Your version')
),
$('<tr>').append(
$('<td>').css({
width: '50%',
padding: '4px 4px 0 8px'
}).append(
$left.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
})
),
$('<td>').css({
width: '50%',
padding: '4px 8px 0 4px'
}).append(
$right.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
})
)
)
).prependTo(editor.find('> :first-child'));
$left.on('input', syncSize);
$right.on('input', syncSize);
syncSize();
showCompare(editor, title, text, r.full);
r.textarea = $left;
return r;
});
}
function clickHandler(event) {
const $element = $(event.target).closest('.qe-editlink');
if (!$element.length || $element.hasClass('qe-loading')) return;
event.preventDefault();
showEditor($element);
}
$.when($.ready).done(function () {
const $body = $(document.body);
$body.on('click', clickHandler);
});
})();
odbt5u9zfwx6bxyru7cpic4smezhxwe
Portugal in the Junior Eurovision Song Contest
0
168785
739047
681512
2026-04-21T23:15:28Z
~2026-24671-15
73651
Add additional test copy
739047
wikitext
text/x-wiki
Test change
i33uiyz4eplv15g26atp4vdu7dkf350
739048
739047
2026-04-21T23:16:27Z
~2026-24671-15
73651
Remove change
739048
wikitext
text/x-wiki
Test
bop1vj5i98maix36pjrpgep1w6hnxfe
739049
739048
2026-04-21T23:22:55Z
~2026-24653-43
73652
added 1 to test hcaptcha
739049
wikitext
text/x-wiki
Test1
hz8wwmgnqpczk13kff6qt0cmp5aj291
User:Ponor/wAwB-worker.js
2
171632
739044
738852
2026-04-21T22:48:40Z
MusikAnimal
24817
CodeMirror v6 modules have been moved ([[phab:T373720]])
739044
javascript
text/javascript
/*
* wAwB – An in-browser application for automated editing of wiki pages.
* Features: customizable regex or JavaScript search-and-replace rules,
* custom JavaScript pre/post-processing functions and function libraries,
* granular protection or targeting of different parts of wikitext,
* a full-fledged CodeMirror editor, and options to move, delete, and protect pages.
* Author: [[User:Ponor]]
* Documentation: [[User:Ponor/wAwB]]
* License: GNU General Public License (GPL)
*/
//<nowiki>
mw.loader.using([
'oojs-ui-core',
'oojs-ui-widgets',
'oojs-ui-windows',
'mediawiki.api',
'mediawiki.diff.styles',
'mediawiki.util',
'oojs-ui.styles.icons-content',
'oojs-ui.styles.icons-interactions',
'oojs-ui.styles.icons-movement',
'oojs-ui.styles.icons-moderation',
'oojs-ui.styles.icons-editing-core',
'oojs-ui.styles.icons-editing-advanced'
]).then(function() {
// =====================================================================
// 1. STATE & CONFIGURATION
// =====================================================================
var SCRIPT_TIMEOUT_MS = window.wa_timeout || 5000;
var FETCH_SAFETY_LIMIT = window.wa_fetchLimit || 10000;
var APP_NAME = "wAwB";
var DO_TAG = false;
var SUMMARY_SUFFIX = window.wa_suffix || " [[:en:User:Ponor/wAwB| #wAwB]]";
var APP_VERSION = "0.6";
var DOC_URL = window.wa_docUrl || "https://en.wikipedia.org/wiki/User:Ponor/wAwB";
document.title = window.wa_editIn || "Edit in wAwB";
var PERMS = {
canSave: false,
allowBot: false,
saveDelay: 0
};
var IS_ADMIN = mw.config.get('wgUserGroups').includes('sysop');
var CAN_MOVE = IS_ADMIN || mw.config.get('wgUserGroups').includes('extendedmover') || mw.config.get('wgUserGroups').includes('filemover') || mw.config.get('wgUserGroups').includes('pagemover');
var WIKI = mw.config.get('wgDBname');
var SAVED_RUN = 0;
var SAVED_SESSION = 0;
var currentPageExists = false;
var isRunning = false;
var isFetching = false;
var currentTitle = null;
var currentVars = {};
var currentLibrary = {
name: null,
code: null
};
var originalWikitext = "";
var baseRevId = 0;
var currentViewMode = 'diff';
var autoSaveTimer = null;
var propNamesLoaded = false;
var hasNewSources = false;
var currentHeightMode = 1; // 0=25%, 1=45% (default), 2=72%
var heightValues = ['25%', '45%', '72%'];
// EXTERNAL RULES STATE
var wikiTypos = [];
var localTypos = [];
// LOADING FLAG
var isLoadingProject = false;
// NAMESPACE ALIASES
var nsIds = mw.config.get('wgNamespaceIds');
var catAliases = [],
fileAliases = [];
for (var key in nsIds) {
if (nsIds[key] === 14) catAliases.push(key.replace(/_/g, ' '));
if (nsIds[key] === 6) fileAliases.push(key.replace(/_/g, ' '));
}
catAliases.sort((a, b) => b.length - a.length);
fileAliases.sort((a, b) => b.length - a.length);
var REGEX_CAT_PFX = catAliases.map(mw.util.escapeRegExp).join('|');
var REGEX_FILE_PFX = fileAliases.map(mw.util.escapeRegExp).join('|');
// MASTER PROTECTION DEFINITIONS
var PROTECTION_DEFS = [{
id: 'nowiki',
isOn: true,
label: 'Nowiki: <nowiki>',
regex: /<nowiki>[\s\S]*?<\/nowiki>|<nowiki\s*\/>/gi
},
{
id: 'comments',
isOn: true,
label: 'Comments: <!' + '-- -->',
regex: new RegExp('<!' + '--[\\s\\S]*?--' + '>', 'g')
},
{
id: 'headers',
isOn: false,
label: 'Headers: == Title ==',
regex: /^==+[\s\S]+?==+\s*$/gm
},
{
id: 'templates',
isOn: false,
label: 'Templates: {{...}}',
open: '{{',
close: '}}',
species: null,
regex: null
},
{
id: 'tables',
isOn: false,
label: 'Tables: {|...|}',
open: '\n{|',
close: '\n|}',
regex: null
},
{
id: 'images',
isOn: false,
label: 'Images: [[File:...|...|...]]',
open: '[[',
close: ']]',
species: '(?:' + REGEX_FILE_PFX + ')\\s*:',
regex: null
},
{
id: 'refs',
isOn: true,
label: 'Refs: <ref...',
regex: /<ref[^>]*?\/>|<ref[^>]*?(?<!\/)>[\s\S]*?<\/ref>/gi
},
{
id: 'blocks',
isOn: false,
label: 'Blocks: math, gallery...',
regex: null
},
{
id: 'categories',
isOn: true,
label: 'Categories: [[Category:...]]',
regex: new RegExp('\\[\\[\\s*(' + REGEX_CAT_PFX + ')\\s*:[^\\]]+\\]\\]', 'giu')
},
{
id: 'files',
isOn: true,
label: 'File names: File:...',
regex: new RegExp('(?<=\\[\\[\\s*:?(:?' + REGEX_FILE_PFX + ')\\s*:)[^|\\]]+' + '|^\\s*(?:' + REGEX_FILE_PFX + ')\\s*:([^\\][}{|\\n]{1,150}\\.(?:svg|png|jpe?g|gif|tiff|webp|xcf|mp3|midi|ogg|webm|flac|wav|mpe?g|pdf|djv))', 'gmiu')
},
{
id: 'targets',
isOn: false,
label: 'Targets of [[...|',
regex: /(?<=\[\[:?)[^|\]]+?(?=\||\]\])/g
},
{
id: 'extlinks',
isOn: true,
label: 'External links: [...]',
regex: /(?<=\[)(https?:\/\/|ftps?:\/\/|mailto:)[^\]]+(?=\])/gi
},
{
id: 'urls',
isOn: true,
label: 'URLs: http...',
regex: /https?:\/\/[^\s<>[\]"'`()]+/gi
}
];
// =====================================================================
// 2. CSS STYLES
// =====================================================================
var styles = `
* { box-sizing: border-box; }
#wa-root { font-family: sans-serif; height: 100vh; width: 100vw; overflow: hidden; display: flex; font-size: 14px; }
#wa-left-panel { width: 400px; min-width: 400px; max-width: 400px; background: var(--background-color-base, #fff); border-right: 1px solid #c8ccd1; display: flex; flex-direction: column; z-index: 10; overflow-x: hidden; }
#wa-left-panel h3 { color: #3f6fcf; text-align: center; margin: 12px 0 0 0; }
#wa-username { color: #3f6fcf; text-align: center; margin: 2px 0; font-size: 92%; }
#wa-content-area { flex: 1; padding: 10px 10px 100px 10px; overflow-y: auto; overflow-x: hidden; }
#wa-right-panel { flex: 1; display: flex; flex-direction: column; height: 100%; background: var(--background-color-interactive, #eaecf0); overflow: hidden; }
#wa-visual-output { flex: 0 0 45%; min-height: 0; overflow-y: auto; background: var(--background-color-base, #fff); padding: 20px; border-bottom: 1px solid #c8ccd1; }
.wa-editor-header { flex: 0 0 40px; gap:4em; min-height: 40px; padding: 0 7px; background: var(--background-color-interactive-subtle, #f8f9fa); border-bottom: 1px solid #c8ccd1; color: var(--color-subtle, #54595d); display: flex; justify-content: space-between; align-items: center; white-space: nowrap; z-index: 10; }
.wa-editor-header.wa-dirty { background: var(--background-color-warning-subtle, #fdf2d5); border-bottom: 1px solid #e6a700; }
.wa-header-left { flex: 1; display: flex; align-items: center; overflow: hidden; }
.wa-title-link { font-weight: bold; font-size: 1.1em; color: var(--color-progressive--focus, #36c) !important; text-decoration: none; text-overflow: ellipsis; overflow: hidden; max-width: 300px; }
.wa-title-link:hover { text-decoration: underline; }
.wa-header-sep { margin: 0 10px; border-right: 1px solid #ccc; height: 16px; display: inline-block; }
.wa-unsaved-wrapper { display: none; align-items: center; }
.wa-dirty .wa-unsaved-wrapper { display: inline-flex; }
.wa-unsaved-dot { color: var(--color-destructive, #bf3c2c); font-size: 1.2em; margin-right: 5px; line-height: 1; }
.wa-unsaved-text { color: var(--color-placeholder, #72777d); font-size: 0.9em; font-weight: normal; }
.wa-header-center { flex: 0 0 auto; text-align: center; }
.wa-status-label { font-weight: bold; font-size: 0.85em; color: var(--color-base, #202122); text-transform: uppercase; letter-spacing: 0.5px; }
.wa-status-error { color: var(--color-error, #bf3c2c); }
.wa-status-working { color: var(--color-progressive--focus, #36c); }
.wa-header-right { flex: 1; text-align: right; font-size: 0.85em; color: var(--color-placeholder, #72777d); display: flex; justify-content: flex-end; align-items: center; gap: 8px; }
.wa-info-container { margin-right: 10px; }
.wa-tools-container { display: flex; align-items: center; gap: 2px; }
.wa-resize-container { display: flex; flex-direction: column; justify-content: center; height: 100%; margin-left: 10px; padding-left: 5px; border-left: 1px solid #ccc; }
.wa-resize-btn { cursor: pointer; color: #72777d; user-select: none; width: 20px; height: 14px; display: flex; align-items: center; justify-content: center; transition: color 0.1s ease-in-out; }
.wa-resize-btn:hover { color: #36c; }
.wa-resize-btn.wa-resize-disabled { color: #ccc; cursor: default; }
#wa-proc-header { margin-top: 15px !important; border-bottom: none !important; cursor: default; }
#wa-proc-title { font-weight: bold; padding: 10px; display: block; }
#wa-proc-content { padding: 0 10px 15px 10px; }
#wa-editor-area { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--background-color-base, #fff); position: relative; overflow: hidden; }
#wa-editor-textarea { flex: 1; height: 100%; font-family: monospace; font-size: 13px; border: none; outline: none; padding: 10px; resize: none; width: 100%; }
.cm-editor { height: 100% !important; flex: 1; }
.wa-section-header { margin-top: 12px; border-bottom: 1px solid #eee; width: 100%; display: block; margin-left: 0 !important; }
#wa-content-area .wa-section-header:first-child, #wa-content-area .wa-section-header.oo-ui-buttonElement-frameless:first-child { margin-top: 0; margin-left: 0 !important; }
.wa-section-header > .oo-ui-buttonElement-button { text-align: left; padding: 10px 10px !important; margin: 0 !important; display: block; width: 100%; position: relative; border-left: 3px solid #3f6fcf !important; border-radius: 3px !important; background-color: transparent !important; }
.wa-section-header > .oo-ui-buttonElement-button:focus { outline: none !important; }
.wa-section-header .oo-ui-labelElement-label { font-weight: bold; padding-left: 0 !important; margin-left: 0 !important; color: var(--color-base, #202122); }
.wa-section-header .oo-ui-indicatorElement-indicator { position: absolute; right: 10px !important; top: 50%; margin-top: -10px; left: auto !important; width: 20px; }
.wa-foldable-content { display: none; padding: 10px 0; }
.wa-source-options { background: var(--background-color-interactive-subtle, #f8f9fa); border: 1px solid #c8ccd1; border-top: none; padding: 8px; margin-bottom: 10px; font-size: 0.9em; }
.wa-opt-row { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 5px; }
.wa-opt-label { font-weight: bold; width: 100%; margin-bottom: 5px; color: var(--color-base, #202122); }
.wa-opt-row > div { margin-top: 8px !important; margin-bottom: 8px !important; }
.wa-rule-row { background: var(--background-color-interactive-subtle, #f8f9fa); border: 1px solid #c8ccd1; padding: 8px; margin-bottom: 8px; border-radius: 4px; display: flex; align-items: stretch; transition: background-color 0.3s; }
.wa-rule-row.wa-highlight { background-color: var(--background-color-interactive, #eaecf0); border-color: #36c; }
.wa-rule-controls { display: flex; flex-direction: column; justify-content: center; gap: 0px; padding-right: 4px; border-right: 1px solid #eee; margin-right: 8px; }
.wa-rule-btn { margin: 0 !important; margin-right: 0 !important; margin-left: 0 !important; }
.wa-rule-btn > .oo-ui-buttonElement-button { margin: 0 !important; }
.wa-rule-content { flex: 1; min-width: 0; }
.wa-rule-opt-row { display: flex; justify-content: space-between; align-items: center; margin-top: 5px; }
#wa-ns-selector { width: 100%; margin-bottom: 10px; font-family: sans-serif; font-size: 0.9em; border: 1px solid #a2a9b1; }
.wa-lib-dialog > .oo-ui-window-frame { width: 80vw !important; max-width: none !important; height: 80vh !important; max-height: none !important; }
.wa-lib-editorwrapper { height: 100%; border: 1px solid #c8ccd1; position: relative; boxSizing: border-box; }
.wa-page-list-raw textarea { font-family: monospace; font-size: 0.9em; white-space: pre; overflow-x: auto; }
.wa-list-running textarea { background-color: var(--background-color-neutral-subtle, #f8f8f8) !important; color: var(--color-base, #202122) !important; }
.wa-grid-container { display: flex; gap: 6px; margin-bottom: 10px; }
.wa-grid-col { flex: 1; display: flex; flex-direction: column; gap: 6px; }
.wa-grid-col .oo-ui-buttonWidget { width: 100%; }
.wa-grid-col .oo-ui-buttonWidget .oo-ui-buttonElement-button { width: 100%; text-align: center; justify-content: center; }
.wa-toolbar { display: flex; justify-content: flex-end; align-items: center; gap: 4px; border-bottom: 1px solid #eee; padding-bottom: 4px; margin-bottom: 4px; }
.wa-list-counter { margin-right: auto; font-weight: bold; color: var(--color-subtle, #54595d); font-size: 0.9em; padding-left: 5px; }
.wa-project-bar { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 10px; margin: 8px 0; justify-content: center; }
.wa-project-bar .oo-ui-buttonElement-button { padding-left: 36px !important; padding-right: 12px !important; font-size: 0.9em; }
.wa-project-bar .oo-ui-iconElement-icon { left: 10px !important; }
.wa-settings-header { font-weight: bold; color: var(--color-subtle, #54595d); margin-bottom: 8px; display: block; text-transform: uppercase; font-size: 0.85em; }
.wa-setting-row { display: flex; align-items: center; margin-bottom: 6px; }
.wa-bot-row { background: var(--background-color-success-subtle, #dff2eb); border: 1px solid #a5d6a7; padding: 8px; margin-bottom: 10px; border-radius: 4px; display: flex; align-items: center; justify-content: flex-start; gap: 15px; }
table.diff { width: 100%; font-family: "Adwaita Mono", "Courier New", monospace }
table.diff td { vertical-align: top; }
table.diff tr:hover td { background-color: var(--background-color-progressive-subtle--hover, #d9e2ff); cursor: pointer; }
@keyframes wa-pulse-red { 0% { box-shadow: 0 0 0 0 rgba(255, 0, 0, 0.4); border-color: #ff0000; } 70% { box-shadow: 0 0 0 6px rgba(255, 0, 0, 0); border-color: #ff0000; } 100% { box-shadow: 0 0 0 0 rgba(255, 0, 0, 0); border-color: #ff0000; } }
.wa-summary-warning input { animation: wa-pulse-red 1s infinite; border-color: #ff0000 !important; }
`;
$('<style>').text(styles).appendTo('head');
$('body').empty();
// =====================================================================
// 3. HELPER FUNCTIONS
// =====================================================================
function checkPermissions() {
return new Promise(function(resolve) {
var api = new mw.Api();
var projectNs = mw.config.get('wgFormattedNamespaces')[4];
var checkTitles = {
'permissions': projectNs + ':AutoWikiBrowser/CheckPageJSON',
'tag': 'MediaWiki:Tag-wAwB'
};
api.get({
action: 'query',
prop: 'revisions',
titles: Object.values(checkTitles).join('|'),
rvprop: 'content',
rvslots: 'main',
formatversion: 2
}).then(function(data) {
var pagePerms = data.query.pages.find(p => p.title === checkTitles['permissions']);
var pageTag = data.query.pages.find(p => p.title === checkTitles['tag']);
DO_TAG = pageTag.missing === undefined;
var userName = mw.config.get('wgUserName');
var userGroups = mw.config.get('wgUserGroups');
var isSysop = userGroups.includes('sysop');
if (!pagePerms.missing) {
try {
var content = pagePerms.revisions[0].slots.main.content;
var json = JSON.parse(content);
var inEnabledUsers = json.enabledusers && json.enabledusers.includes(userName);
var inEnabledBots = json.enabledbots && json.enabledbots.includes(userName);
var isBotGroup = userGroups.includes('bot');
var canSave = inEnabledUsers || inEnabledBots || isSysop;
var allowBot = inEnabledBots && isBotGroup;
resolve({
canSave: canSave,
allowBot: allowBot,
saveDelay: 0
});
} catch (e) {
resolve({
canSave: false,
allowBot: false,
saveDelay: 0
});
}
} else {
var editCount = mw.config.get('wgUserEditCount');
if (editCount > 500) resolve({
canSave: true,
allowBot: false,
saveDelay: 20000
});
else resolve({
canSave: false,
allowBot: false,
saveDelay: 0
});
}
}).catch(function() {
resolve({
canSave: false,
allowBot: false,
saveDelay: 0
});
});
});
}
function getUserCode(widget, globalName) {
var val = widget.getValue().trim();
if (!val || val.startsWith('// Enter')) {
if (window[globalName] && typeof window[globalName] === 'function') {
var s = window[globalName].toString();
return s.substring(s.indexOf('{') + 1, s.lastIndexOf('}'));
}
return "";
}
if (val.startsWith('function')) {
return val.substring(val.indexOf('{') + 1, val.lastIndexOf('}'));
}
return val;
}
function normalizeLine(line) {
if (!line) return null;
// Pass through comments/STOP commands (trimmed)
if (line.trim().startsWith('####')) return line.trim();
// Handle Title|Variables
var parts = line.split('|');
var title = parts[0].trim();
if (!title) return null; // Skip if title is empty
// Reassemble: Clean Title + Original Variables (preserving whitespace)
var rest = parts.length > 1 ? parts.slice(1).join('|') : null;
return title + (rest !== null ? '|' + rest : '');
}
function getNormalizedList(text) {
if (!text) return [];
return text.split('\n')
.map(normalizeLine)
.filter(function(l) {
return l !== null;
});
}
function getDeduplicatedList(text) {
if (!text) return [];
var seen = new Set();
var out = [];
var lines = text.split('\n');
for (var i = 0; i < lines.length; i++) {
var clean = normalizeLine(lines[i]);
if (clean && !seen.has(clean)) {
seen.add(clean);
out.push(clean);
}
}
return out;
}
function parseTypoContent(content) {
if (!content) return [];
try {
var $wrapper = $('<body>').html(content);
var rules = [];
$wrapper.find('Typo:not([disabled])').each(function() {
var $t = $(this);
var find = $t.attr('find');
var replace = $t.attr('replace');
if (find && replace !== undefined) {
rules.push({
find: find,
replace: replace,
regex: true,
flags: 'gmu',
enabled: true,
isFunc: false
});
}
});
return rules;
} catch (e) {
return [];
}
}
// =====================================================================
// 4. UI CONSTRUCTION
// =====================================================================
checkPermissions().then(function(pState) {
PERMS = pState;
var $main = $('<div>').attr('id', 'wa-root').appendTo('body');
var $left = $('<div>').attr('id', 'wa-left-panel').appendTo($main);
$left.append($('<h3>').append($('<a>').attr('href', DOC_URL).attr('target', '_blank').text(APP_NAME).css({
'text-decoration': 'none',
'color': 'inherit'
})));
$left.append($('<div>').attr('id', 'wa-username').append($('<a>').attr('href', mw.util.getUrl('Special:Contributions/' + mw.config.get('wgUserName'))).attr('target', '_blank').text('User: ' + mw.config.get('wgUserName')).css({
'text-decoration': 'none',
'color': 'inherit'
})));
var btnSaveProj = new OO.ui.ButtonWidget({
icon: 'download',
label: 'Save project',
framed: false,
flags: 'progressive'
});
var btnLoadProj = new OO.ui.ButtonWidget({
icon: 'upload',
label: 'Load project',
framed: false
});
var $projBar = $('<div>').addClass('wa-project-bar').append(btnSaveProj.$element, btnLoadProj.$element);
$left.append($projBar);
var $fileInput = $('<input type="file" accept=".json">').hide().appendTo('body');
var $content = $('<div>').attr('id', 'wa-content-area').appendTo($left);
var $right = $('<div>').attr('id', 'wa-right-panel').appendTo($main);
var $editorHeader = $('<div>').addClass('wa-editor-header').appendTo($right);
var $headerLeft = $('<div>').addClass('wa-header-left').appendTo($editorHeader);
var $titleLink = $('<a>').addClass('wa-title-link').text('Page content').attr('target', '_blank').appendTo($headerLeft);
$('<span>').addClass('wa-header-sep').appendTo($headerLeft);
var $unsavedWrapper = $('<span>').addClass('wa-unsaved-wrapper').appendTo($headerLeft);
$('<span>').addClass('wa-unsaved-dot').text('●').appendTo($unsavedWrapper);
$('<span>').addClass('wa-unsaved-text').text('Unsaved').appendTo($unsavedWrapper);
var $headerCenter = $('<div>').addClass('wa-header-center').appendTo($editorHeader);
var $statusLabel = $('<span>').addClass('wa-status-label').text('READY').appendTo($headerCenter);
var $headerRight = $('<div>').addClass('wa-header-right').appendTo($editorHeader);
var $infoContainer = $('<span>').addClass('wa-info-container').appendTo($headerRight);
var $toolsContainer = $('<div>').addClass('wa-tools-container').appendTo($headerRight);
var $resizeContainer = $('<div>').addClass('wa-resize-container').appendTo($headerRight);
var $adminTools = $('<div>').addClass('wa-admin-tools').hide().appendTo($toolsContainer);
// Wide chevron SVGs
var svgUp = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="8" viewBox="0 0 24 12"><path d="M2 10 L12 2 L22 10" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var svgDown = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="8" viewBox="0 0 24 12"><path d="M2 2 L12 10 L22 2" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg>';
var $btnSizeUp = $('<div>').addClass('wa-resize-btn').html(svgUp).attr('title', 'Decrease view size');
var $btnSizeDown = $('<div>').addClass('wa-resize-btn').html(svgDown).attr('title', 'Increase view size');
$resizeContainer.append($btnSizeUp, $btnSizeDown);
function setPanelHeight(modeIndex) {
currentHeightMode = modeIndex;
if (currentHeightMode < 0) currentHeightMode = 0;
if (currentHeightMode > 2) currentHeightMode = 2;
$('#wa-visual-output').css('flex-basis', heightValues[currentHeightMode]);
$btnSizeUp.toggleClass('wa-resize-disabled', currentHeightMode === 0);
$btnSizeDown.toggleClass('wa-resize-disabled', currentHeightMode === 2);
}
$btnSizeUp.on('click', function() {
if (!$(this).hasClass('wa-resize-disabled')) setPanelHeight(currentHeightMode - 1);
});
$btnSizeDown.on('click', function() {
if (!$(this).hasClass('wa-resize-disabled')) setPanelHeight(currentHeightMode + 1);
});
setPanelHeight(1);
if (CAN_MOVE) {
var btnAdminMove = new OO.ui.ButtonWidget({
icon: 'move',
title: 'Move page to $xA',
disabled: true,
framed: false
});
$adminTools.append(btnAdminMove.$element).show();
}
if (IS_ADMIN) {
var btnAdminDel = new OO.ui.ButtonWidget({
icon: 'trash',
title: 'Delete page',
disabled: true,
framed: false
});
var btnAdminProt = new OO.ui.ButtonWidget({
icon: 'lock',
title: 'Protect page',
disabled: true,
framed: false
});
$adminTools.append(btnAdminDel.$element, btnAdminProt.$element).show();
}
var btnWatch = new OO.ui.ButtonWidget({
icon: 'star',
title: 'Watch this page',
framed: false,
disabled: true,
accessKey: 'w'
});
$toolsContainer.append(btnWatch.$element);
var $visualOut = $('<div>').attr('id', 'wa-visual-output').html('<div style="color:#aaa; text-align:center; margin-top:50px;">Ready to start...</div>').prependTo($right);
var $editorArea = $('<div>').attr('id', 'wa-editor-area').appendTo($right);
var $textArea = $('<textarea>').attr('id', 'wa-editor-textarea').attr('placeholder', 'Page text will appear here...').appendTo($editorArea);
function setStatus(msg, type) {
if (!msg) msg = "Ready";
$statusLabel.text(msg).removeClass('wa-status-error wa-status-working');
if (type === 'error') $statusLabel.addClass('wa-status-error');
if (type === 'working') $statusLabel.addClass('wa-status-working');
}
// EDITOR OBJECT
var Editor = {
mode: 'textarea',
cmInstance: null,
init: function() {
var self = this;
mw.loader.using(['ext.CodeMirror', 'ext.CodeMirror.mode.mediawiki']).then(function(require) {
try {
self.cmInstance = new(require('ext.CodeMirror'))($textArea[0], (require('ext.CodeMirror.mode.mediawiki')).mediawiki());
self.cmInstance.initialize();
self.mode = 'codemirror';
} catch (e) {
console.error("CM Error", e);
}
}).catch(function(err) {
console.error("CM Load Error:", err);
});
$textArea.on('input', updateDirtyState);
},
getValue: function() {
return (this.mode === 'codemirror' && this.cmInstance) ? this.cmInstance.view.state.doc.toString() : $textArea.val();
},
setValue: function(text) {
$textArea.val(text);
if (this.mode === 'codemirror' && this.cmInstance) {
this.cmInstance.view.dispatch({
changes: {
from: 0,
to: this.cmInstance.view.state.doc.length,
insert: text
}
});
} else {
$textArea[0].dispatchEvent(new Event('input'));
}
},
setDisabled: function(d) {
$textArea.prop('disabled', d);
if (this.mode === 'codemirror' && this.cmInstance) {
this.cmInstance.view.contentDOM.contentEditable = !d;
$($textArea).parent().find('.cm-editor').css('opacity', d ? 0.5 : 1);
}
},
scrollToLine: function(n) {
if (isNaN(n)) return;
if (this.mode === 'codemirror' && this.cmInstance) {
var v = this.cmInstance.view;
var l = v.state.doc.line(n);
v.dispatch({
effects: v.constructor.scrollIntoView(l.from, {
y: 'center'
}),
selection: {
anchor: l.from
}
});
v.focus();
}
}
};
var WorkerEngine = {
activeWorker: null,
workerURL: null,
currentLibCode: null,
timeoutTimer: null,
initWorker: function(libCode) {
this.destroy(); // Clean up existing if any
this.currentLibCode = libCode || "";
var scriptContent = this.currentLibCode + "\n\n" + `
self.onmessage = async function(e) {
try {
var data = e.data;
var inputs = data.texts || [data.text];
var vars = data.vars;
var outputs = [];
// Helper to construct async functions dynamically
var AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
function inject(str) {
if (!str) return "";
return str.replace(/\\$x([A-Z]|x)/g, function(m) { return vars[m] || ""; });
}
// Returns a Promise and handles 'await' inside user code
async function execUserFunc(code, currentText, currentVars, sharedObj) {
if (!code || code.trim() === "") return currentText;
try {
var func = new AsyncFunction('text', 'vars', 'shared', code);
var res = await func(currentText, currentVars, sharedObj);
if (res && typeof res === 'object' && res.skip) {
return { _skipSignal: true, reason: res.reason || 'Script-requested skip' };
}
return (res !== undefined) ? res : currentText;
} catch (err) {
throw err; // or: return currentText
}
}
for (var i = 0; i < inputs.length; i++) {
var text = inputs[i];
var shared = {}; // Shared context for this page
// 1. Pre-Process
var preRes;
if (data.preCode && data.preCode.trim() !== "") {
preRes = await execUserFunc(data.preCode, text, vars, shared);
} else if (typeof wAwB_Pre === 'function') {
try {
preRes = await wAwB_Pre(text, vars, shared);
if (preRes && typeof preRes === 'object' && preRes.skip) {
preRes = { _skipSignal: true, reason: preRes.reason || 'Script-requested skip' };
}
} catch (err) { preRes = text; }
} else {
preRes = text;
}
if (preRes && preRes._skipSignal) {
self.postMessage({ skipped: true, reason: preRes.reason });
return;
}
text = (preRes !== undefined) ? preRes : text;
// 2. Rules Processing
if (data.rules && data.rules.length > 0) {
data.rules.forEach(function(rule) {
var findStr = inject(rule.find);
if (!findStr) return;
if (rule.isFunc) {
try {
var userFunc = new Function('match', 'groups', 'vars', 'shared', rule.replace);
text = text.replace(new RegExp(findStr, (rule.flags || 'gmu').replace(/[^gimsuvy]/g, '')), function(...args) {
var match = args[0];
var groups = args.slice(1, -2);
try {
var res = userFunc(match, groups, vars, shared);
return res !== undefined ? res : match;
} catch (err) { return match; }
});
} catch (e) {}
} else {
var repStr = inject(rule.replace).replace(/\\\\n/g, "\\n").replace(/\\\\t/g, "\\t").replace(/\\\\r/g, "\\r");
if (rule.regex) {
try {
var flags = (rule.flags || 'gmu').replace(/[^gimsuvy]/g, '');
text = text.replace(new RegExp(findStr, flags), repStr);
} catch (e) {}
} else {
var finalFind = findStr.replace(/\\\\n/g, "\\n").replace(/\\\\t/g, "\\t").replace(/\\\\r/g, "\\r");
text = text.split(finalFind).join(repStr);
}
}
});
}
// 3. Post-Process
var postRes;
if (data.postCode && data.postCode.trim() !== "") {
postRes = await execUserFunc(data.postCode, text, vars, shared);
} else if (typeof wAwB_Post === 'function') {
try {
postRes = await wAwB_Post(text, vars, shared);
if (postRes && typeof postRes === 'object' && postRes.skip) {
postRes = { _skipSignal: true, reason: postRes.reason || 'Script-requested skip' };
}
} catch (err) { postRes = text; }
} else {
postRes = text;
}
if (postRes && postRes._skipSignal) {
self.postMessage({ skipped: true, reason: postRes.reason });
return;
}
text = (postRes !== undefined) ? postRes : text;
outputs.push(text);
}
self.postMessage({ success: true, texts: outputs });
} catch (err) { self.postMessage({ success: false, error: err.toString() }); }
};
`;
var blob = new Blob([scriptContent], {
type: 'application/javascript'
});
this.workerURL = URL.createObjectURL(blob);
this.activeWorker = new Worker(this.workerURL);
},
run: function(payload) {
var self = this;
return new Promise(function(resolve, reject) {
// Re-init if no worker exists, or if the user changed the library code
if (!self.activeWorker || self.currentLibCode !== (payload.libraryCode || "")) {
self.initWorker(payload.libraryCode);
}
if (self.timeoutTimer) clearTimeout(self.timeoutTimer);
self.timeoutTimer = setTimeout(function() {
self.destroy(); // Assassinate the stuck worker
reject("Script timed out (" + SCRIPT_TIMEOUT_MS + "ms).");
}, SCRIPT_TIMEOUT_MS);
self.activeWorker.onmessage = function(e) {
clearTimeout(self.timeoutTimer);
if (e.data.skipped) resolve({
skipped: true,
reason: e.data.reason
});
else if (e.data.success) resolve({
success: true,
texts: e.data.texts
});
else reject(e.data.error);
};
self.activeWorker.postMessage(payload);
});
},
destroy: function() {
if (this.activeWorker) {
this.activeWorker.terminate();
this.activeWorker = null;
}
if (this.workerURL) {
URL.revokeObjectURL(this.workerURL);
this.workerURL = null;
}
if (this.timeoutTimer) {
clearTimeout(this.timeoutTimer);
this.timeoutTimer = null;
}
}
};
var PageProtector = {
store: [],
getKey: function() {
var id = this.store.length.toString();
var p = "";
for (var i = 0; i < id.length; i++) {
p += String.fromCharCode(0xE010 + parseInt(id[i]));
}
return '\uE000' + p + '\uE001';
},
protect: function(text, mode, config, templateSpecies = null) {
this.store = [];
var self = this;
var safeRep = function(t, r) {
return t.replace(r, function(m) {
if (!m) return m;
var key = self.getKey();
self.store.push(m);
return key;
});
};
var shouldProcess = function(id) {
if (mode === 'target') return config === id;
return config[id] === true;
};
var matchedBrackets = function(text, op, cl, species = '') {
var newText = "",
depth = 0,
start = 0,
cursor = 0;
var speciesRegex = species ? new RegExp(species, 'iu') : null;
for (var i = 0; i < text.length; i++) {
if (text[i] === op[0] && text.slice(i, i + op.length) === op) {
if (depth === 0) start = i;
depth++;
i += op.length - 1;
} else if (text[i] === cl[0] && text.slice(i, i + cl.length) === cl) {
if (depth > 0) {
depth--;
if (depth === 0) {
var chunk = text.substring(start, i + cl.length);
if (!speciesRegex || speciesRegex.test(chunk)) {
var key = self.getKey();
self.store.push(chunk);
newText += text.substring(cursor, start) + key;
} else {
newText += text.substring(cursor, i + cl.length);
}
cursor = i + cl.length;
}
i += cl.length - 1;
}
}
}
newText += text.substring(cursor);
return newText;
};
PROTECTION_DEFS.forEach(function(def) {
if (shouldProcess(def.id)) {
if (def.id === 'blocks') {
['math', 'pre', 'source', 'syntaxhighlight', 'code', 'gallery'].forEach(t => text = safeRep(text, new RegExp('<' + t + '[^>]*?>[\\s\\S]*?<\\/' + t + '>|<' + t + '[^>]*?/>', 'gi')));
} else if (['templates', 'tables', 'images'].includes(def.id)) {
var activeSpecies = (def.id === 'templates') ? templateSpecies : def.species;
text = matchedBrackets(text, def.open, def.close, activeSpecies || '');
} else if (def.regex) {
text = safeRep(text, def.regex);
}
}
});
return text;
},
restore: function(text) {
var self = this;
var loop = 100;
while (/(\uE000[\uE010-\uE019]+\uE001)/.test(text) && loop > 0) {
text = text.replace(/\uE000([\uE010-\uE019]+)\uE001/g, function(m, d) {
var id = "";
for (var i = 0; i < d.length; i++) id += (d.charCodeAt(i) - 0xE010).toString();
return self.store[parseInt(id, 10)] || m;
});
loop--;
}
return text;
}
};
var accordionRegistry = [];
function addSection(title, $inner) {
var btn = new OO.ui.ButtonWidget({
label: title,
indicator: 'down',
framed: false,
classes: ['wa-section-header']
});
var box = $('<div>').addClass('wa-foldable-content').append($inner);
var sectionObj = {
btn: btn,
box: box,
label: title
};
accordionRegistry.push(sectionObj);
btn.on('click', function() {
var isOpening = !box.is(':visible');
if (isOpening) {
accordionRegistry.forEach(function(sec) {
if (sec !== sectionObj) {
sec.box.hide();
sec.btn.setIndicator('down');
}
});
}
box.toggle();
btn.setIndicator(box.is(':visible') ? 'up' : 'down');
});
$content.append(btn.$element, box);
return sectionObj;
}
// WIDGETS
var srcSelect = new OO.ui.DropdownInputWidget({
options: [{
data: 'cat',
label: 'Category'
}, {
data: 'linksto',
label: 'Pages linking to...'
}, {
data: 'linkson',
label: 'Links on page...'
}, {
data: 'prefix',
label: 'Pages with prefix...'
}, {
data: 'watchlist',
label: 'Watchlist'
}, {
data: 'search',
label: 'Wiki search'
}, {
data: 'usercontribs',
label: 'User contributions'
}, {
data: 'pageswithprop',
label: 'Pages with property'
}]
});
var srcInput = new OO.ui.TextInputWidget({
placeholder: 'Category...'
});
var now = new Date();
var today = now.toISOString().split('T')[0];
var srcInputUser = new OO.ui.TextInputWidget({
placeholder: 'Username'
});
var srcInputStartDate = new OO.ui.TextInputWidget({
value: today + 'T00:00:00',
placeholder: 'ISO start date'
});
var srcInputEndDate = new OO.ui.TextInputWidget({
value: today + 'T23:59:59',
placeholder: 'ISO end date'
});
var srcDropProp = new OO.ui.DropdownInputWidget({
options: []
});
var $optContainer = $('<div>').addClass('wa-source-options').hide();
var $optCat = $('<div>').hide();
var $optUser = $('<div>').hide();
var $optProp = $('<div>').hide();
var chkCatPages = new OO.ui.CheckboxInputWidget({
selected: true
});
var chkCatSub = new OO.ui.CheckboxInputWidget({
selected: false
});
var chkCatFile = new OO.ui.CheckboxInputWidget({
selected: false
});
$optCat.append($('<div>').addClass('wa-opt-label').text('Include:'), new OO.ui.FieldLayout(chkCatPages, {
label: 'Pages',
align: 'inline'
}).$element, new OO.ui.FieldLayout(chkCatSub, {
label: 'Subcats',
align: 'inline'
}).$element, new OO.ui.FieldLayout(chkCatFile, {
label: 'Files',
align: 'inline'
}).$element);
$optUser.append(new OO.ui.FieldLayout(srcInputUser, {
label: 'User',
align: 'top'
}).$element, new OO.ui.FieldLayout(srcInputStartDate, {
label: 'Start (Older)',
align: 'top'
}).$element, new OO.ui.FieldLayout(srcInputEndDate, {
label: 'End (Newer)',
align: 'top'
}).$element);
$optProp.append(new OO.ui.FieldLayout(srcDropProp, {
label: 'Property',
align: 'top'
}).$element);
var $optLinks = $('<div>').hide();
var chkLinkWiki = new OO.ui.CheckboxInputWidget({
selected: true
});
var chkLinkTrans = new OO.ui.CheckboxInputWidget({
selected: false
});
var chkLinkImg = new OO.ui.CheckboxInputWidget({
selected: false
});
var dropLinkRedir = new OO.ui.DropdownInputWidget({
options: [{
data: 'nonredirects',
label: 'No redirects'
}, {
data: 'all',
label: 'Both'
}, {
data: 'redirects',
label: 'Redirects only'
}]
});
var chkLinkToRedir = new OO.ui.CheckboxInputWidget({
selected: false
});
$optLinks.append($('<div>').addClass('wa-opt-label').text('What to include:'), $('<div>').addClass('wa-opt-row').append(new OO.ui.FieldLayout(chkLinkWiki, {
label: 'Wikilinks',
align: 'inline'
}).$element, new OO.ui.FieldLayout(chkLinkTrans, {
label: 'Transclusions',
align: 'inline'
}).$element, new OO.ui.FieldLayout(chkLinkImg, {
label: 'File usage',
align: 'inline'
}).$element), $('<div>').addClass('wa-opt-label').text('Redirects:'), dropLinkRedir.$element, new OO.ui.FieldLayout(chkLinkToRedir, {
label: 'Include links to redirects',
align: 'inline'
}).$element);
$optContainer.append($optCat, $optLinks, $optUser, $optProp);
var queryCache = {};
var lastMode = 'cat';
srcSelect.on('change', function(newMode) {
if (!isLoadingProject) {
if (lastMode !== 'watchlist' && lastMode !== 'usercontribs' && lastMode !== 'pageswithprop') {
queryCache[lastMode] = srcInput.getValue();
}
}
$optContainer.hide();
$optCat.hide();
$optLinks.hide();
$optUser.hide();
$optProp.hide();
srcInput.setDisabled(false).$element.show();
if (newMode === 'cat') {
$optContainer.show();
$optCat.show();
} else if (newMode === 'linksto') {
$optContainer.show();
$optLinks.show();
} else if (newMode === 'usercontribs') {
$optContainer.show();
$optUser.show();
srcInput.setDisabled(true).$element.hide();
} else if (newMode === 'pageswithprop') {
$optContainer.show();
$optProp.show();
srcInput.setDisabled(true).$element.hide();
if (!propNamesLoaded) {
new mw.Api().get({
action: 'query',
list: 'pagepropnames',
ppnlimit: 'max'
}).then(function(d) {
if (d.query && d.query.pagepropnames) {
srcDropProp.setOptions(d.query.pagepropnames.map(p => ({
data: p.propname,
label: p.propname
})));
propNamesLoaded = true;
}
});
}
}
if (newMode === 'watchlist') {
srcInput.setValue('');
srcInput.setDisabled(true);
srcInput.$input.attr('placeholder', '(No query needed)');
} else if (newMode !== 'usercontribs' && newMode !== 'pageswithprop') {
srcInput.setValue(queryCache[newMode] || '');
var ph = 'Query...';
if (newMode === 'cat') ph = 'Category name';
if (newMode === 'search') ph = 'Search query...';
if (newMode === 'prefix') ph = 'Page prefix...';
if (newMode === 'linksto') ph = 'Pages linking to this title...';
if (newMode === 'linkson') ph = 'Get links from this page...';
srcInput.$input.attr('placeholder', ph);
}
lastMode = newMode;
});
srcSelect.emit('change', srcSelect.getValue());
var $nsSelect = $('<select>').attr('id', 'wa-ns-selector').attr('multiple', 'multiple').attr('size', '8');
var nsMap = mw.config.get('wgFormattedNamespaces');
for (var id in nsMap) {
if (parseInt(id) >= 0) $nsSelect.append($('<option>').val(id).text(id + ': ' + (nsMap[id] || '(Main)')));
}
$nsSelect.val(['0']);
var btnAdd = new OO.ui.ButtonWidget({
label: 'Add to list',
icon: 'add',
flags: ['primary', 'progressive']
});
var $btnRow = $('<div>').css({
'display': 'flex',
'justify-content': 'flex-end',
'margin-top': '10px'
});
var $fetchStatus = $('<span>').css({
'margin-right': '10px',
'color': '#888',
'font-size': '0.85em',
'align-self': 'center'
}).hide();
$btnRow.append($fetchStatus, btnAdd.$element);
addSection('Source', $('<div>').append(new OO.ui.FieldLayout(srcSelect, {
label: 'Mode',
align: 'top'
}).$element, new OO.ui.FieldLayout(srcInput, {
label: 'Query',
align: 'top'
}).$element, $optContainer, $('<div>').text('Namespaces:').css({
'font-weight': 'bold',
'margin-top': '5px'
}), $nsSelect, $btnRow));
var redirMode = new OO.ui.RadioSelectWidget({
items: [new OO.ui.RadioOptionWidget({
data: 'edit',
label: 'Edit the redirect page (Default)'
}), new OO.ui.RadioOptionWidget({
data: 'follow',
label: 'Follow redirect (Edit target)'
}), new OO.ui.RadioOptionWidget({
data: 'skip',
label: 'Skip redirects'
})]
});
redirMode.selectItemByData('edit');
var radSkipExist = new OO.ui.RadioSelectWidget({
items: [new OO.ui.RadioOptionWidget({
data: 'none',
label: 'Process all'
}), new OO.ui.RadioOptionWidget({
data: 'missing',
label: 'Skip if page does not exist'
}), new OO.ui.RadioOptionWidget({
data: 'exists',
label: 'Skip if page exists'
})]
});
radSkipExist.selectItemByData('none');
var chkSkipNoChange = new OO.ui.CheckboxInputWidget({
selected: false
});
var inpSkipContains = new OO.ui.TextInputWidget({
placeholder: 'Text/Regex for Skip if FOUND'
});
var togSkipContainsRegex = new OO.ui.ToggleSwitchWidget({
value: false,
title: 'Use regex'
});
var inpSkipNotContains = new OO.ui.TextInputWidget({
placeholder: 'Text/Regex for Skip if MISSING'
});
var togSkipNotContainsRegex = new OO.ui.ToggleSwitchWidget({
value: false,
title: 'Use regex'
});
var inpSkipCategories = new OO.ui.TextInputWidget({
placeholder: 'Skip if in: Category1|Category2'
});
var inpSkipNotCategories = new OO.ui.TextInputWidget({
placeholder: 'Skip if NOT in: Category1|Category2'
});
var $settingsPanel = $('<div>')
.append($('<span>').addClass('wa-settings-header').text('Redirects'))
.append(redirMode.$element)
.append($('<hr>').css('border-top', '1px solid #eee'))
.append($('<span>').addClass('wa-settings-header').text('Skip logic'))
.append(new OO.ui.FieldLayout(chkSkipNoChange, {
label: 'Skip if no changes made',
align: 'inline'
}).$element.css('margin-bottom', '8px'))
.append(radSkipExist.$element)
.append($('<hr>').css('border-top', '1px solid #eee'))
.append($('<span>').addClass('wa-settings-header').text('Content filters'))
.append($('<div>').addClass('wa-setting-row').append(inpSkipContains.$element.css('flex', 1), togSkipContainsRegex.$element.css('margin-left', '5px')))
.append($('<div>').addClass('wa-setting-row').append(inpSkipNotContains.$element.css('flex', 1), togSkipNotContainsRegex.$element.css('margin-left', '5px')))
.append($('<hr>').css('border-top', '1px solid #eee'))
.append($('<span>').addClass('wa-settings-header').text('Category filters'))
.append(new OO.ui.FieldLayout(inpSkipCategories, {
label: 'Blacklist',
align: 'top'
}).$element)
.append(new OO.ui.FieldLayout(inpSkipNotCategories, {
label: 'Whitelist',
align: 'top'
}).$element);
addSection('Skip', $settingsPanel);
var dropProtMode = new OO.ui.DropdownInputWidget({
options: [{
data: 'protect',
label: 'Protect (Exclude)'
}, {
data: 'target',
label: 'Target (Edit Matches Only)'
}]
});
var inpTemplateFilter = new OO.ui.TextInputWidget({
placeholder: 'Regex: infobox rail line|railway'
});
var $templateFilterLayout = new OO.ui.FieldLayout(inpTemplateFilter, {
label: 'Template filter',
align: 'top'
});
var $protList = $('<div>');
var protCheckboxes = {};
PROTECTION_DEFS.forEach(function(def) {
var chk = new OO.ui.CheckboxInputWidget({
selected: def.isOn
});
protCheckboxes[def.id] = chk;
$protList.append(new OO.ui.FieldLayout(chk, {
label: def.label,
align: 'inline'
}).$element);
});
var targetRadioItems = PROTECTION_DEFS.map(function(def) {
return new OO.ui.RadioOptionWidget({
data: def.id,
label: def.label
});
});
var radTargetSet = new OO.ui.RadioSelectWidget({
items: targetRadioItems
});
var $targetList = $('<div>').hide().append(radTargetSet.$element);
dropProtMode.on('change', function(mode) {
if (mode === 'protect') {
$protList.show();
$targetList.hide();
} else {
$protList.hide();
$targetList.show();
}
});
addSection('Protection', $('<div>').addClass('wa-source-options')
.append(new OO.ui.FieldLayout(dropProtMode, {
label: 'Mode',
align: 'top'
}).$element)
.append($('<hr>').css('border-top', '1px solid #eee'))
.append($protList).append($targetList)
.append($('<div style="margin-top:10px;">').append($templateFilterLayout.$element))
);
var $rulesList = $('<div>');
var btnAddRule = new OO.ui.ButtonWidget({
label: 'Add rule',
icon: 'add'
});
var rulesRegistry = [];
addSection('Rules', $('<div>').append($rulesList, btnAddRule.$element));
var togWikiTypos = new OO.ui.ToggleSwitchWidget({
value: false
});
var lblWikiStatus = $('<div>').css({
'font-size': '0.85em',
'color': '#888',
'margin-top': '2px'
});
var btnLoadLocal = new OO.ui.ButtonWidget({
icon: 'upload',
label: 'Load file',
framed: false
});
var btnClearLocal = new OO.ui.ButtonWidget({
icon: 'trash',
title: 'Clear local',
framed: false,
flags: 'destructive',
disabled: true
});
var lblLocalStatus = $('<div>').text('No local rules').css({
'font-size': '0.85em',
'color': '#888',
'margin-top': '2px'
});
var $typoInput = $('<input type="file">').hide().appendTo('body');
var $extRulesPanel = $('<div>').addClass('wa-source-options');
$extRulesPanel.append(
$('<div>').css({
'display': 'flex',
'align-items': 'center',
'justify-content': 'space-between'
}).append($('<span>').text('Project:AutoWikiBrowser/Typos').css('font-weight', 'bold'), togWikiTypos.$element),
$('<div>').css('margin-bottom', '10px').append(lblWikiStatus),
$('<hr>').css('border-top', '1px solid #eee'),
$('<div>').append($('<div>').css({
'display': 'flex',
'align-items': 'center'
}).append($('<span>').text('Local rules (session only)').css({
'font-weight': 'bold'
}), $('<div>').css('flex', '1'), btnLoadLocal.$element, btnClearLocal.$element), lblLocalStatus)
);
addSection('External rules', $extRulesPanel);
var txtPreScript = new OO.ui.MultilineTextInputWidget({
rows: 6,
value: '',
placeholder: '// Enter JavaScript function body here.\n// Available variables: text, vars, shared\nreturn text;'
});
var txtPostScript = new OO.ui.MultilineTextInputWidget({
rows: 6,
value: '',
placeholder: '// Enter JavaScript function body here.\n// Available variables: text, vars, shared\nreturn text;'
});
var btnLoadLib = new OO.ui.ButtonWidget({
icon: 'upload',
title: 'Load library (.js)',
framed: false
});
var btnRemoveLib = new OO.ui.ButtonWidget({
icon: 'trash',
title: 'Remove library',
framed: false,
flags: 'destructive'
});
var txtLibStatus = new OO.ui.TextInputWidget({
value: '(No library loaded)',
readOnly: true
});
var $libInput = $('<input type="file" accept=".js">').hide().appendTo('body');
var btnEditLib = new OO.ui.ButtonWidget({
icon: 'edit',
label: 'Edit project library',
framed: false
});
var $scriptPanel = $('<div>').append(
$('<div>').css({
'display': 'flex',
'align-items': 'center',
'gap': '5px',
'margin-bottom': '10px'
}).append($('<span>').text('JS library:').css({
'font-weight': 'bold',
'white-space': 'nowrap'
}), txtLibStatus.$element.css('flex', '1'), btnLoadLib.$element, btnRemoveLib.$element),
$('<div>').css({
'display': 'flex',
'justify-content': 'flex-end',
'margin-bottom': '10px'
}).append(btnEditLib.$element),
new OO.ui.FieldLayout(txtPreScript, {
label: 'Pre-Process',
align: 'top'
}).$element,
new OO.ui.FieldLayout(txtPostScript, {
label: 'Post-Process',
align: 'top'
}).$element
);
addSection('Scripts', $scriptPanel);
function updateLibUI() {
if (currentLibrary.code) {
txtLibStatus.setValue(currentLibrary.name);
btnRemoveLib.setDisabled(false);
} else {
txtLibStatus.setValue('(No library loaded)');
btnRemoveLib.setDisabled(true);
}
}
updateLibUI();
function LibraryEditorDialog(config) {
LibraryEditorDialog.super.call(this, config);
}
OO.inheritClass(LibraryEditorDialog, OO.ui.ProcessDialog);
LibraryEditorDialog.static.name = 'libraryEditor';
LibraryEditorDialog.static.title = 'Edit project library';
LibraryEditorDialog.static.actions = [{
action: 'save',
label: 'Save',
flags: ['primary', 'progressive']
},
{
label: 'Cancel',
flags: 'safe'
}
];
LibraryEditorDialog.prototype.initialize = function() {
LibraryEditorDialog.super.prototype.initialize.call(this);
this.$element.addClass('wa-lib-dialog'); // Attach our custom CSS override class
this.panel = new OO.ui.PanelLayout({
padded: true,
expanded: true
});
this.$editorWrapper = $('<div>').addClass('wa-lib-editorwrapper');
this.panel.$element.append(this.$editorWrapper);
this.$body.append(this.panel.$element);
};
LibraryEditorDialog.prototype.getSetupProcess = function(data) {
data = data || {};
return LibraryEditorDialog.super.prototype.getSetupProcess.call(this, data)
.next(function() {
var self = this;
self.$editorWrapper.empty();
// Create a textarea for the MediaWiki CM wrapper to properly bind to
var $libTextArea = $('<textarea>').appendTo(self.$editorWrapper);
var initCode = currentLibrary.code || "// All custom library functions defined here will be passed to the worker.\n// Special functions:\n// function wAwB_Pre(text, vars, shared) { return text; }\n// function wAwB_Post(text, vars, shared) { return text; }\n";
return mw.loader.using(['ext.CodeMirror', 'ext.CodeMirror.modes']).then(function(require) {
var CM = require('ext.CodeMirror');
var modes = require('ext.CodeMirror.modes');
self.cmInstance = new CM($libTextArea[0], modes.javascript());
self.cmInstance.initialize();
self.cmInstance.view.dispatch({
changes: {
from: 0,
insert: initCode
}
});
// Force CodeMirror to fill the wrapper
self.$editorWrapper.find('.cm-editor').css({
height: '100%'
});
}).catch(function(err) {
console.error("wAwB CM Init Error:", err);
});
}, this);
};
LibraryEditorDialog.prototype.getActionProcess = function(action) {
var dialog = this;
if (action === 'save') {
return new OO.ui.Process(function() {
var newCode = "";
if (dialog.cmInstance) {
newCode = dialog.cmInstance.view.state.doc.toString();
}
if (newCode.trim() === "") {
currentLibrary = {
name: null,
code: null
};
} else {
currentLibrary.code = newCode;
currentLibrary.name = "custom code";
}
updateLibUI();
dialog.close({
action: action
});
});
}
if (action === 'cancel' || !action) {
return new OO.ui.Process(function() {
dialog.close({
action: action
});
});
}
return LibraryEditorDialog.super.prototype.getActionProcess.call(this, action);
};
LibraryEditorDialog.prototype.getTeardownProcess = function(data) {
return LibraryEditorDialog.super.prototype.getTeardownProcess.call(this, data)
.next(function() {
if (this.cmInstance) {
try {
this.cmInstance.view.destroy();
} catch (e) {}
this.cmInstance = null;
}
}, this);
};
var windowManager = new OO.ui.WindowManager();
$('body').append(windowManager.$element);
var libDialog = new LibraryEditorDialog();
windowManager.addWindows([libDialog]);
btnEditLib.on('click', function() {
windowManager.openWindow(libDialog);
});
var togAdminEnable = new OO.ui.ToggleSwitchWidget({
value: false
});
var chkMovRedirect = new OO.ui.CheckboxInputWidget({
selected: false
});
var chkMovTalk = new OO.ui.CheckboxInputWidget({
selected: true
});
var chkMovSub = new OO.ui.CheckboxInputWidget({
selected: false
});
var chkDelTalk = new OO.ui.CheckboxInputWidget({
selected: true
});
var dropProtEdit = new OO.ui.DropdownInputWidget({
options: [{
data: '',
label: '(No Change)'
}, {
data: 'all',
label: 'All'
}, {
data: 'autoconfirmed',
label: 'Autoconfirmed'
}, {
data: 'sysop',
label: 'Sysop'
}]
});
var dropProtMove = new OO.ui.DropdownInputWidget({
options: [{
data: '',
label: '(No Change)'
}, {
data: 'all',
label: 'All'
}, {
data: 'autoconfirmed',
label: 'Autoconfirmed'
}, {
data: 'sysop',
label: 'Sysop'
}]
});
var inpProtExpiry = new OO.ui.TextInputWidget({
placeholder: 'infinite / 2 days / 12 hours'
});
if (CAN_MOVE || IS_ADMIN) {
var $adminPanel = $('<div>').append(
$('<div>').css({
'display': 'flex',
'align-items': 'center',
'justify-content': 'flex-start',
'gap': '10px'
}).append($('<span>').text('Enable page actions').css('font-weight', 'bold'), togAdminEnable.$element),
$('<hr>')
);
if (CAN_MOVE) {
$adminPanel.append(
$('<strong>').text('Move options:'), new OO.ui.FieldLayout(chkMovRedirect, {
label: 'Do not create redirect',
align: 'inline'
}).$element, new OO.ui.FieldLayout(chkMovTalk, {
label: 'Move talk page',
align: 'inline'
}).$element, new OO.ui.FieldLayout(chkMovSub, {
label: 'Move subpages',
align: 'inline'
}).$element, $('<br>')
);
}
if (IS_ADMIN) {
$adminPanel.append(
$('<strong>').text('Delete options:'), new OO.ui.FieldLayout(chkDelTalk, {
label: 'Delete talk page',
align: 'inline'
}).$element, $('<br>'),
$('<strong>').text('Protect options:'), new OO.ui.FieldLayout(dropProtEdit, {
label: 'Edit level',
align: 'top'
}).$element, new OO.ui.FieldLayout(dropProtMove, {
label: 'Move level',
align: 'top'
}).$element, new OO.ui.FieldLayout(inpProtExpiry, {
label: 'Expiry',
align: 'top'
}).$element
);
}
addSection('Page actions', $adminPanel);
}
var btnPower = new OO.ui.ButtonWidget({
label: 'Start',
icon: 'power',
flags: ['primary', 'progressive'],
title: 'Start editing',
accessKey: 'a'
});
var btnDiff = new OO.ui.ButtonWidget({
label: 'Diff',
icon: 'update',
title: 'Show diff',
accessKey: 'd'
});
var btnSkip = new OO.ui.ButtonWidget({
label: 'Next',
icon: 'next',
title: 'Skip to next page',
accessKey: 'n',
disabled: true
});
var btnPreview = new OO.ui.ButtonWidget({
label: 'Preview',
icon: 'article',
title: 'Preview page',
accessKey: 'p'
});
var btnSave = new OO.ui.ButtonWidget({
label: 'Save',
icon: 'upload',
flags: 'progressive',
title: 'Save edit',
accessKey: 's',
disabled: true
});
var inputSummary = new OO.ui.TextInputWidget({
placeholder: '',
value: '',
title: 'Enter edit summary',
accessKey: 'b'
});
var $sumLayout = new OO.ui.FieldLayout(inputSummary, {
label: 'Edit summary',
align: 'top'
}).$element;
$sumLayout.css('margin-bottom', '6px');
var listTextarea = new OO.ui.MultilineTextInputWidget({
rows: 15,
classes: ['wa-page-list-raw']
});
var btnSort = new OO.ui.ButtonWidget({
icon: 'sortVertical',
framed: false
});
var btnDedup = new OO.ui.ButtonWidget({
icon: 'funnel',
framed: false
});
var btnClear = new OO.ui.ButtonWidget({
icon: 'trash',
framed: false
});
var btnPreParse = new OO.ui.ButtonWidget({
label: 'Pre-parse',
title: 'Process list in background',
icon: 'robot',
framed: false
});
var $listCounter = $('<span>').addClass('wa-list-counter').text('0 pages');
var togAutoSave = new OO.ui.ToggleSwitchWidget({
value: false
});
var txtAutoDelay = new OO.ui.TextInputWidget({
value: '10'
});
var $botRow = $('<div>').addClass('wa-bot-row').hide();
if (PERMS.allowBot) {
$botRow.show().append($('<span>').css('font-weight', 'bold').text('Bot mode: '), togAutoSave.$element, $('<span>').text('Delay (s):'), txtAutoDelay.$element.css('max-width', '40px'));
togAutoSave.on('change', function(v) {
if (v) txtAutoDelay.setValue('10');
});
}
var sortAsc = true;
var $procHeader = $('<div>').addClass('wa-section-header').attr('id', 'wa-proc-header').css({
'display': 'flex',
'justify-content': 'space-between',
'align-items': 'center'
});
var $procTitle = $('<span>').attr('id', 'wa-proc-title').text('Processing');
var chkMinor = new OO.ui.CheckboxInputWidget({
selected: true,
title: 'Minor edit'
});
var $minorLayout = new OO.ui.FieldLayout(chkMinor, {
label: 'm',
align: 'inline',
title: 'Minor edit'
});
$minorLayout.$element.css({
'margin-right': '15px',
'font-weight': 'normal'
});
$procHeader.append($procTitle, $minorLayout.$element);
var $procContent = $('<div>').attr('id', 'wa-proc-content').append(
$sumLayout, $botRow,
$('<div>').addClass('wa-grid-container').append(
$('<div>').addClass('wa-grid-col').append(btnPower.$element),
$('<div>').addClass('wa-grid-col').append(btnDiff.$element, btnSkip.$element),
$('<div>').addClass('wa-grid-col').append(btnPreview.$element, btnSave.$element)
),
$('<div>').addClass('wa-toolbar').append($listCounter, btnSort.$element, btnDedup.$element, btnClear.$element),
listTextarea.$element,
$('<div>').css({
'margin-top': '5px'
}).append(btnPreParse.$element)
);
$content.append($procHeader, $procContent);
var configWidgets = [
srcSelect, srcInput, srcInputUser, srcInputStartDate, srcInputEndDate, srcDropProp,
chkCatPages, chkCatSub, chkCatFile, chkLinkWiki, chkLinkTrans, chkLinkImg, dropLinkRedir, chkLinkToRedir,
btnAdd, redirMode, chkSkipNoChange, radSkipExist,
inpSkipContains, togSkipContainsRegex, inpSkipNotContains, togSkipNotContainsRegex, inpSkipCategories, inpSkipNotCategories,
dropProtMode, radTargetSet, inpTemplateFilter, btnAddRule,
txtPreScript, txtPostScript, chkMovRedirect, chkMovTalk, chkMovSub, chkDelTalk, dropProtEdit, dropProtMove, inpProtExpiry,
togWikiTypos, btnLoadLocal, btnClearLocal, btnPreParse
];
// =====================================================================
// 5. FUNCTION DEFINITIONS (Core Logic)
// =====================================================================
function checkSummaryWarning() {
var val = inputSummary.getValue();
var isBlank = !val || val.trim() === "";
if (isBlank || hasNewSources) inputSummary.$element.addClass('wa-summary-warning');
else inputSummary.$element.removeClass('wa-summary-warning');
}
function renderCurrentView() {
if (currentViewMode === 'preview') renderPreview();
else renderDiff();
}
function toggleConfig(isLocked) {
configWidgets.forEach(function(w) {
if (w instanceof OO.ui.TextInputWidget || w instanceof OO.ui.MultilineTextInputWidget) {
w.setReadOnly(isLocked);
w.$element.css('opacity', isLocked ? 0.8 : 1);
} else {
w.setDisabled(isLocked);
}
});
$nsSelect.prop('disabled', isLocked);
for (var key in protCheckboxes) protCheckboxes[key].setDisabled(isLocked);
rulesRegistry.forEach(function(r) {
r.find.setReadOnly(isLocked);
r.rep.setReadOnly(isLocked);
r.regex.setDisabled(isLocked);
r.flags.setReadOnly(isLocked);
r.enable.setDisabled(isLocked);
r.del.setDisabled(isLocked);
r.btnFunc.setDisabled(isLocked || !r.regex.getValue());
r.btnUp.setDisabled(isLocked || rulesRegistry.indexOf(r) === 0);
r.btnDown.setDisabled(isLocked || rulesRegistry.indexOf(r) === rulesRegistry.length - 1);
});
if (CAN_MOVE || IS_ADMIN) togAdminEnable.setDisabled(isLocked);
btnLoadLib.setDisabled(isLocked);
btnRemoveLib.setDisabled(isLocked || !currentLibrary.code);
btnEditLib.setDisabled(isLocked);
btnLoadLocal.setDisabled(isLocked);
btnClearLocal.setDisabled(isLocked || localTypos.length === 0);
}
function updateListCount() {
var val = listTextarea.getValue();
var count = val.trim() ? val.split('\n').filter(function(l) {
var line = l.trim();
return line !== "" && !line.startsWith("####");
}).length : 0;
$listCounter.text(count + ' pages');
}
listTextarea.on('change', updateListCount);
function updateDirtyState() {
if (isRunning && currentTitle && Editor.getValue() !== originalWikitext) $editorHeader.addClass('wa-dirty');
else $editorHeader.removeClass('wa-dirty');
}
function removeTopLine() {
var l = listTextarea.getValue().split('\n');
l.shift();
listTextarea.setValue(l.join('\n'));
updateListCount();
}
function updateInterfaceMode() {
var isAdminMode = togAdminEnable.getValue();
var pageLoaded = !!currentTitle;
btnSave.setDisabled(isAdminMode || !pageLoaded || !PERMS.canSave);
btnSkip.setDisabled(!pageLoaded);
btnPreview.setDisabled(!pageLoaded);
btnDiff.setDisabled(isAdminMode || !pageLoaded);
Editor.setDisabled(isAdminMode || !pageLoaded);
if (CAN_MOVE) {
var allowAdmin = isAdminMode && currentPageExists;
btnAdminMove.setDisabled(!(allowAdmin && currentVars['$xA']));
if (currentVars['$xA']) btnAdminMove.setTitle('Move page to ' + currentVars['$xA']);
else btnAdminMove.setTitle('Move page to $xA (Variable not set)');
}
if (IS_ADMIN) {
var allowAdmin = isAdminMode && currentPageExists;
btnAdminDel.setDisabled(!allowAdmin);
btnAdminProt.setDisabled(!allowAdmin);
}
}
function renderDiff() {
$visualOut.html('<div style="color:#888; text-align:center;">Generating Diff...</div>');
var currentText = Editor.getValue();
new mw.Api().post({
'action': 'compare',
fromtitle: currentTitle,
toslots: 'main',
'totext-main': currentText,
slots: 'main',
prop: 'diff',
formatversion: 2
}).then(function(data) {
var diffBody = data.compare && data.compare.bodies && data.compare.bodies.main;
if (diffBody) {
$visualOut.html('<h4>Diff: ' + currentTitle + '</h4><table class="diff"><colgroup><col class="diff-marker"><col class="diff-content"><col class="diff-marker"><col class="diff-content"></colgroup><tbody>' + diffBody + '</tbody></table>');
processDiffTable();
} else {
$visualOut.html('<div style="color:green; text-align:center; padding-top:20px;">No Changes detected</div>');
}
});
}
function processDiffTable() {
var rightLineNum = 0;
$visualOut.find('table.diff tr').each(function() {
var $tr = $(this);
var $linenos = $tr.find('td.diff-lineno');
if ($linenos.length > 0) {
var txt = $linenos.last().text();
var m = txt.match(/(\d+)/);
if (m) rightLineNum = parseInt(m[1]);
return;
}
if ($tr.find('.diff-addedline').length > 0 || $tr.find('.diff-context').length > 0) {
$tr.attr('data-line', rightLineNum);
$tr.css('cursor', 'pointer').attr('title', 'Jump to line ' + rightLineNum);
rightLineNum++;
}
});
// Attach a single delegated click listener to the table instead of every row
$visualOut.find('table.diff').on('click', 'tr[data-line]', function() {
Editor.scrollToLine(parseInt($(this).attr('data-line')));
});
}
function renderPreview() {
$visualOut.html('<div style="color:#888; text-align:center;">Generating Preview...</div>');
new mw.Api().post({
action: 'parse',
title: currentTitle,
text: Editor.getValue(),
prop: 'text|categorieshtml|modules|jsconfigvars',
useskin: mw.config.get('skin'),
disablelimitreport: true,
pst: true,
contentmodel: 'wikitext'
}).then(function(data) {
if (data.parse && data.parse.text) {
var $prev = $('<div>').html(data.parse.text['*']);
if (data.parse.categorieshtml) $prev.append(data.parse.categorieshtml['*']);
$prev.find('a').attr('target', '_blank');
$visualOut.empty().append($prev);
mw.loader.using(data.parse.modules.concat(data.parse.modulestyles, data.parse.modulescripts), function() {
mw.hook('wikipage.content').fire($('.wa-visual-output .mw-parser-output'));
});
}
}).catch(function(err) {
$visualOut.html('Error generating preview.');
alert("Preview failed: " + err);
});
}
async function transformPageText(rawText, title, config) {
var filters = config.filters;
if (filters) {
var check = function(text, rule) {
if (!rule || !rule.val) return false;
if (rule.regex) {
try {
return new RegExp(rule.val, 'mu').test(text);
} catch (e) {
return false;
}
}
return text.indexOf(rule.val) !== -1;
};
if (filters.skipContains && filters.skipContains.val && check(rawText, filters.skipContains)) {
return {
skipped: true,
reason: 'Contains: ' + filters.skipContains.val
};
}
if (filters.skipNotContains && filters.skipNotContains.val && !check(rawText, filters.skipNotContains)) {
return {
skipped: true,
reason: 'Missing: ' + filters.skipNotContains.val
};
}
}
var mode = config.mode;
var inputs = [];
var compiledSpecies = null;
if (config.templateFilter) {
var tFilter = config.templateFilter;
if (tFilter[0] === "^") tFilter = "^\\{\\{\\s*" + tFilter.slice(1);
else tFilter = "\\{\\{\\s*" + tFilter;
compiledSpecies = tFilter + "(?=\\s*[|}\\n])";
}
var skeleton = PageProtector.protect(rawText, mode, config.excludes, compiledSpecies);
if (mode === 'target') inputs = PageProtector.store;
else inputs = [skeleton];
var combinedRules = rulesRegistry.filter(r => r.isActive()).map(r => ({
find: r.find.getValue(),
replace: r.rep.getValue(),
regex: r.regex.getValue(),
flags: r.flags.getValue(),
enabled: r.isActive(),
isFunc: r.isFunc()
}));
if (togWikiTypos.getValue()) combinedRules = combinedRules.concat(wikiTypos);
if (localTypos.length > 0) combinedRules = combinedRules.concat(localTypos);
var payload = {
texts: inputs,
vars: config.vars,
preCode: getUserCode(txtPreScript, 'wAwB_Pre'),
libraryCode: currentLibrary.code,
rules: combinedRules,
postCode: getUserCode(txtPostScript, 'wAwB_Post')
};
var result = await WorkerEngine.run(payload);
if (result.skipped) return {
skipped: true,
reason: result.reason
};
var finalText = "";
if (mode === 'target') {
PageProtector.store = result.texts;
finalText = PageProtector.restore(skeleton);
} else {
finalText = PageProtector.restore(result.texts[0]);
}
return {
skipped: false,
text: finalText
};
}
async function processPageContent() {
try {
setStatus('Processing...', 'working');
var mode = dropProtMode.getValue();
var activeConfig = {
mode: mode,
excludes: {},
templateFilter: inpTemplateFilter.getValue().trim(),
vars: currentVars,
filters: {
skipContains: {
val: inpSkipContains.getValue(),
regex: togSkipContainsRegex.getValue()
},
skipNotContains: {
val: inpSkipNotContains.getValue(),
regex: togSkipNotContainsRegex.getValue()
}
}
};
if (mode === 'protect') {
for (var k in protCheckboxes) activeConfig.excludes[k] = protCheckboxes[k].isSelected();
} else {
var sel = radTargetSet.findSelectedItem();
activeConfig.excludes = sel ? sel.getData() : null;
}
var res = await transformPageText(originalWikitext, currentTitle, activeConfig);
if (res.skipped) {
removeTopLine();
loadNextPage();
return;
}
if (chkSkipNoChange.isSelected() && res.text === originalWikitext) {
removeTopLine();
loadNextPage();
return;
}
setStatus('Ready');
Editor.setValue(res.text);
if (CAN_MOVE || IS_ADMIN) updateInterfaceMode();
else {
Editor.setDisabled(false);
btnSave.setDisabled(!PERMS.canSave);
btnSkip.setDisabled(false);
btnPreview.setDisabled(false);
btnDiff.setDisabled(false);
}
updateDirtyState();
renderCurrentView();
if (PERMS.allowBot && togAutoSave.getValue()) {
var delay = Math.max(0, parseInt(txtAutoDelay.getValue(), 10) || 0) * 1000;
setStatus('Auto-save in ' + (delay / 1000) + 's...', 'working');
if (autoSaveTimer) clearTimeout(autoSaveTimer);
autoSaveTimer = setTimeout(function() {
if (isRunning && PERMS.canSave) {
btnSave.emit('click');
}
}, delay);
}
} catch (e) {
setStatus('Error', 'error');
alert(e);
btnPower.emit('click');
}
}
async function runPreParseBatch() {
// 1. Toggle / Stop Logic
if (isRunning) {
isRunning = false;
setStatus('Stopping...', 'working');
btnPreParse.setLabel('Pre-parse');
return;
}
// 2. Start & Deduplicate
var currentVal = listTextarea.getValue();
var cleanVal = getDeduplicatedList(currentVal).join('\n');
listTextarea.setValue(cleanVal);
updateListCount();
isRunning = true;
toggleUI(true);
// 3. Lock UI
toggleUI(true);
btnSkip.setDisabled(true);
btnDiff.setDisabled(true);
btnPreview.setDisabled(true);
btnSave.setDisabled(true);
Editor.setDisabled(true);
btnPreParse.setLabel('Stop pre-parse');
// Inject STOP marker if not present
var currentList = listTextarea.getValue().split('\n');
if (!currentList.includes('####STOP')) {
currentList.push('####STOP');
listTextarea.setValue(currentList.join('\n'));
}
// Gather Config
var activeConfig = {
mode: dropProtMode.getValue(),
excludes: {},
templateFilter: inpTemplateFilter.getValue().trim(),
vars: {},
filters: {
skipContains: {
val: inpSkipContains.getValue(),
regex: togSkipContainsRegex.getValue()
},
skipNotContains: {
val: inpSkipNotContains.getValue(),
regex: togSkipNotContainsRegex.getValue()
}
}
};
if (activeConfig.mode === 'protect') {
for (var k in protCheckboxes) activeConfig.excludes[k] = protCheckboxes[k].isSelected();
} else {
var sel = radTargetSet.findSelectedItem();
activeConfig.excludes = sel ? sel.getData() : null;
}
setStatus('Pre-parsing...', 'working');
while (isRunning) {
var lines = listTextarea.getValue().split('\n');
var batchTitles = [];
var stopFound = false;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (line === '####STOP') {
stopFound = true;
break;
}
if (line && !line.startsWith('####')) {
var parts = line.split('|');
batchTitles.push({
fullLine: line,
title: parts[0],
vars: parts.slice(1)
});
}
if (batchTitles.length >= 50) break;
}
if (batchTitles.length === 0) {
if (stopFound) setStatus('Pre-parse complete');
else setStatus('List empty');
break;
}
$listCounter.text('Fetching ' + batchTitles.length + '...');
var badCats = inpSkipCategories.getValue().split('|').map(s => s.trim()).filter(s => s);
var reqCats = inpSkipNotCategories.getValue().split('|').map(s => s.trim()).filter(s => s);
var api = new mw.Api();
try {
var data = await api.get({
action: 'query',
prop: 'revisions' + (badCats.length + reqCats.length > 0 ? '|categories' : ''),
titles: batchTitles.map(t => t.title).join('|'),
rvprop: 'content',
rvslots: 'main',
redirects: 1,
cllimit: 'max'
});
var pageMap = {};
if (data.query && data.query.pages) Object.values(data.query.pages).forEach(p => pageMap[p.title] = p);
var redirMap = {};
if (data.query && data.query.redirects) data.query.redirects.forEach(r => redirMap[r.from] = r.to);
var keptLines = [];
for (var k = 0; k < batchTitles.length; k++) {
var item = batchTitles[k];
var lookupTitle = redirMap[item.title] || item.title;
var page = pageMap[lookupTitle];
if (!page || page.missing || page.invalid || !page.revisions || !page.revisions[0]) {
console.warn("Skipping invalid/missing page:", item.title);
continue;
}
var pageCats = new Set((page.categories || []).map(c => c.title.replace(/^[^:]+:/, '').trim()));
if (badCats.some(c => pageCats.has(c))) continue; // Skip
if (reqCats.length > 0 && !reqCats.some(c => pageCats.has(c))) continue; // Skip
var rawText = page.revisions[0].slots.main['*'];
activeConfig.vars = {
'$xx': item.title
};
item.vars.forEach((v, idx) => activeConfig.vars['$x' + String.fromCharCode(65 + idx)] = v);
var res = await transformPageText(rawText, item.title, activeConfig);
// UPDATED LOGIC: Respect "Skip if no change" checkbox
if (!res.skipped && (!chkSkipNoChange.isSelected() || res.text !== rawText)) {
keptLines.push(item.fullLine);
}
}
var freshLines = listTextarea.getValue().split('\n');
var stopIndex = -1;
for (var x = 0; x < freshLines.length; x++) {
if (freshLines[x] === '####STOP') {
stopIndex = x;
break;
}
}
if (stopIndex > -1) {
var topChunk = freshLines.slice(0, stopIndex);
var botChunk = freshLines.slice(stopIndex + 1);
var processedSet = new Set(batchTitles.map(t => t.fullLine));
var newTop = topChunk.filter(l => !processedSet.has(l));
var newList = newTop.concat(['####STOP']).concat(botChunk).concat(keptLines);
listTextarea.setValue(newList.join('\n'));
updateListCount();
}
} catch (e) {
console.error(e);
setStatus('Batch error: ' + e, 'error');
break;
}
}
isRunning = false;
toggleUI(false);
WorkerEngine.destroy();
btnPreParse.setLabel('Pre-parse');
if (listTextarea.getValue().startsWith('####STOP')) setStatus('Pre-parse done!');
else setStatus('Stopped');
}
btnPreParse.on('click', runPreParseBatch);
function loadNextPage() {
if (!isRunning) return;
var allLines = listTextarea.getValue().split('\n');
var listChanged = false;
var stopCommand = false;
while (allLines.length > 0) {
var line = allLines[0];
if (line === '####STOP') {
stopCommand = true;
break;
}
if (line.startsWith('####') || line === "") {
allLines.shift();
listChanged = true;
} else {
break;
}
}
if (listChanged) {
listTextarea.setValue(allLines.join('\n'));
updateListCount();
}
if (stopCommand) {
btnPower.emit('click');
setStatus("Stopped by ####STOP");
return;
}
if (allLines.length === 0) {
btnPower.emit('click');
setStatus("Done!");
return;
}
var raw = allLines[0];
var parts = raw.split('|');
currentTitle = parts[0].trim();
baseRevId = 0;
originalWikitext = "";
if (!currentTitle) {
removeTopLine();
loadNextPage();
return;
}
currentVars = {};
currentVars['$xx'] = currentTitle;
for (var i = 1; i < parts.length; i++) currentVars['$x' + String.fromCharCode(64 + i)] = parts[i];
setStatus('Loading...', 'working');
btnSave.setDisabled(true);
btnPreview.setDisabled(true);
btnDiff.setDisabled(true);
btnSkip.setDisabled(true);
Editor.setDisabled(true);
$titleLink.attr('href', mw.util.getUrl(currentTitle)).text(currentTitle);
$editorHeader.removeClass('wa-dirty');
$visualOut.empty();
Editor.setValue('Loading...');
$infoContainer.empty();
currentPageExists = false;
btnWatch.setDisabled(true);
if (CAN_MOVE || IS_ADMIN) updateInterfaceMode();
var badCats = inpSkipCategories.getValue().split('|').map(s => s.trim()).filter(s => s);
var reqCats = inpSkipNotCategories.getValue().split('|').map(s => s.trim()).filter(s => s);
var api = new mw.Api();
var params = {
action: 'query',
prop: 'revisions|info' + (badCats.length + reqCats.length > 0 ? '|categories' : ''),
titles: currentTitle,
rvprop: 'content|timestamp|ids',
rvslots: 'main',
inprop: 'watched',
cllimit: 'max'
};
var rMode = redirMode.findSelectedItem().getData();
if (rMode === 'follow') params.redirects = 1;
return api.get(params).then(async function(data) {
var pid = Object.keys(data.query.pages)[0];
var page = data.query.pages[pid];
currentPageExists = !page.missing && !page.invalid;
var pageCats = new Set((page.categories || []).map(c => c.title.replace(/^[^:]+:/, '').trim()));
if (badCats.some(c => pageCats.has(c))) {
setStatus('Skip: cat blacklist');
removeTopLine();
loadNextPage();
return;
}
if (reqCats.length > 0 && !reqCats.some(c => pageCats.has(c))) {
setStatus('Skip: cat whitelist');
removeTopLine();
loadNextPage();
return;
}
if (rMode === 'follow' && data.query.redirects) {
currentTitle = page.title;
$titleLink.attr('href', mw.util.getUrl(currentTitle)).text(currentTitle);
mw.notify('Redirect followed to: ' + currentTitle);
}
if (rMode === 'skip' && page.redirect !== undefined) {
removeTopLine();
loadNextPage();
return;
}
var skipMode = radSkipExist.findSelectedItem().getData();
if (pid === "-1") {
if (skipMode === 'missing') {
removeTopLine();
loadNextPage();
return;
}
originalWikitext = "";
baseRevId = 0;
} else {
if (skipMode === 'exists') {
removeTopLine();
loadNextPage();
return;
}
originalWikitext = page.revisions[0].slots.main['*'];
baseRevId = page.revisions[0].revid;
}
if (page.revisions && page.revisions.length > 0) {
var rev = page.revisions[0];
var ts = new Date(rev.timestamp).toISOString().replace('T', ' ').substring(0, 16);
$infoContainer.empty().append('Last edit: ' + ts + ' | ', $('<a>').attr('href', mw.util.getUrl(currentTitle, {
action: 'history'
})).attr('target', '_blank').text('history'));
}
btnWatch.setDisabled(!currentPageExists);
if (page.watched !== undefined) btnWatch.setIcon('unStar');
else btnWatch.setIcon('star');
if (CAN_MOVE || IS_ADMIN) {
updateInterfaceMode();
if (togAdminEnable.getValue()) {
Editor.setValue(originalWikitext);
renderCurrentView();
setStatus('Ready (Page actions)');
return;
}
}
processPageContent();
}).catch(function(e) {
setStatus('API error', 'error');
alert('Load error: ' + e);
btnPower.emit('click');
});
}
async function fetchWithContinue(api, params) {
var allTitles = new Set();
var continueToken = {};
var safetyLimit = FETCH_SAFETY_LIMIT;
var count = 0;
isFetching = true;
btnAdd.setLabel('Cancel fetch');
$fetchStatus.text('Fetching...').show();
try {
while (isFetching && count < safetyLimit) {
var merged = Object.assign({}, params, continueToken);
var data = await api.get(merged);
var batch = [];
if (data.watchlistraw) batch = data.watchlistraw;
else if (data.query) {
if (data.query.pages) batch = Object.values(data.query.pages);
else if (data.query.categorymembers) batch = data.query.categorymembers;
else if (data.query.backlinks) batch = data.query.backlinks;
else if (data.query.embeddedin) batch = data.query.embeddedin;
else if (data.query.imageusage) batch = data.query.imageusage;
else if (data.query.search) batch = data.query.search;
else if (data.query.allpages) batch = data.query.allpages;
else if (data.query.usercontribs) batch = data.query.usercontribs;
else if (data.query.pageswithprop) batch = data.query.pageswithprop;
}
if (batch.length > 0) {
batch.forEach(item => {
if (item.title) allTitles.add(item.title);
});
count = allTitles.size;
$fetchStatus.text('Fetched ' + count + '...');
}
if (data.continue) continueToken = data.continue;
else break;
}
} catch (e) {
alert("Fetch interrupted: " + e);
}
isFetching = false;
btnAdd.setLabel('Add to list').setDisabled(false);
$fetchStatus.text('Added ' + allTitles.size + ' pages').delay(3000).fadeOut();
if (allTitles.size > 0) {
hasNewSources = true;
checkSummaryWarning();
}
return Array.from(allTitles);
}
function toggleUI(d) {
if (d) {
btnPower.setLabel('Stop').setIcon('power').setFlags(['destructive']);
} else {
btnPower.setLabel('Start').setIcon('power').clearFlags().setFlags(['primary', 'progressive']);
if (PERMS.allowBot) togAutoSave.setValue(false);
}
toggleConfig(d);
btnSort.setDisabled(d);
btnDedup.setDisabled(d);
btnClear.setDisabled(d);
btnSaveProj.setDisabled(d);
btnLoadProj.setDisabled(d);
btnSkip.setDisabled(!d);
btnSave.setDisabled(true);
listTextarea.setReadOnly(d);
if (d) listTextarea.$element.addClass('wa-list-running');
else listTextarea.$element.removeClass('wa-list-running');
}
function resetPanels() {
Editor.setValue('');
$titleLink.text('Page content').removeAttr('href');
$editorHeader.removeClass('wa-dirty');
setStatus('Ready');
currentTitle = null;
$visualOut.html('<div style="color:#aaa; text-align:center; margin-top:50px;">Ready...</div>');
$infoContainer.empty();
btnWatch.setDisabled(true);
Editor.setDisabled(true);
currentPageExists = false;
if (CAN_MOVE || IS_ADMIN) updateInterfaceMode();
toggleUI(false);
updateListCount();
if (autoSaveTimer) clearTimeout(autoSaveTimer);
}
function arrayMove(arr, old_index, new_index) {
if (new_index >= arr.length) {
var k = new_index - arr.length + 1;
while (k--) arr.push(undefined);
}
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
}
function updateRuleButtons() {
rulesRegistry.forEach(function(item, idx) {
item.btnUp.setDisabled(idx === 0);
item.btnDown.setDisabled(idx === rulesRegistry.length - 1);
});
}
function addRule() {
var row = $('<div>').addClass('wa-rule-row');
var controls = $('<div>').addClass('wa-rule-controls');
var btnUp = new OO.ui.ButtonWidget({
icon: 'collapse',
framed: false,
title: 'Move up',
classes: ['wa-rule-btn']
});
var btnDown = new OO.ui.ButtonWidget({
icon: 'expand',
framed: false,
title: 'Move down',
classes: ['wa-rule-btn']
});
controls.append(btnUp.$element, btnDown.$element);
var contentDiv = $('<div>').addClass('wa-rule-content');
var f = new OO.ui.TextInputWidget({
placeholder: 'Find'
});
var r = new OO.ui.TextInputWidget({
placeholder: 'Replace'
});
var reg = new OO.ui.ToggleSwitchWidget();
var fl = new OO.ui.TextInputWidget({
value: 'gmu',
disabled: true
}).toggle(false);
var btnEnable = new OO.ui.ButtonWidget({
icon: 'power',
framed: false,
title: 'Toggle rule',
flags: ['progressive']
});
var isRuleActive = true;
var btnFunc = new OO.ui.ButtonWidget({
icon: 'code',
framed: false,
title: 'Toggle JS mode',
disabled: true
});
var isRuleFunc = false;
var toggleRule = function(forceVal) {
var val = (forceVal !== undefined) ? forceVal : !isRuleActive;
isRuleActive = val;
row.css('opacity', isRuleActive ? 1 : 0.5);
if (isRuleActive) btnEnable.setFlags(['progressive']);
else btnEnable.clearFlags();
};
btnEnable.on('click', function() {
toggleRule();
});
var toggleFunc = function(forceVal) {
var val = (forceVal !== undefined) ? forceVal : !isRuleFunc;
isRuleFunc = val;
if (isRuleFunc) {
btnFunc.setFlags(['progressive']);
r.$input.attr('placeholder', 'return match.toUpperCase();');
} else {
btnFunc.clearFlags();
r.$input.attr('placeholder', 'Replace');
}
};
btnFunc.on('click', function() {
toggleFunc();
});
btnFunc.toggle(false);
reg.on('change', function(v) {
fl.setDisabled(!v);
fl.toggle(v);
btnFunc.setDisabled(!v);
if (!v) {
btnFunc.toggle(false);
if (isRuleFunc) toggleFunc(false);
} else btnFunc.toggle(true);
});
var del = new OO.ui.ButtonWidget({
icon: 'trash',
flags: 'destructive',
framed: false
});
del.on('click', function() {
row.fadeOut(200, function() {
row.remove();
rulesRegistry = rulesRegistry.filter(x => x.row !== row);
updateRuleButtons();
});
});
contentDiv.append(f.$element, $('<div>').css('margin-top', '3px').append(r.$element), $('<div>').addClass('wa-rule-opt-row').append($('<div>').css({
'display': 'flex',
'align-items': 'center'
}).append($('<div>').css({
'display': 'flex',
'align-items': 'center'
}).append($('<span>').text('Regex: ').css({
'font-size': '0.8em',
'margin-right': '4px'
}), reg.$element, fl.$element.css({
'width': '50px',
'margin-left': '5px'
})), btnFunc.$element.css('margin-left', '10px')), $('<div>').css('display', 'flex').append(btnEnable.$element, del.$element)));
row.append(controls, contentDiv);
$rulesList.append(row);
var ruleItem = {
row: row,
find: f,
rep: r,
regex: reg,
flags: fl,
btnUp: btnUp,
btnDown: btnDown,
enable: btnEnable,
del: del,
btnFunc: btnFunc,
isActive: function() {
return isRuleActive;
},
setActive: toggleRule,
isFunc: function() {
return isRuleFunc;
},
setFunc: toggleFunc
};
rulesRegistry.push(ruleItem);
btnUp.on('click', function() {
var idx = rulesRegistry.indexOf(ruleItem);
if (idx > 0) {
var prevRow = rulesRegistry[idx - 1].row;
row.fadeOut(150, function() {
row.insertBefore(prevRow).fadeIn(150).addClass('wa-highlight');
setTimeout(function() {
row.removeClass('wa-highlight');
}, 500);
});
arrayMove(rulesRegistry, idx, idx - 1);
updateRuleButtons();
}
});
btnDown.on('click', function() {
var idx = rulesRegistry.indexOf(ruleItem);
if (idx < rulesRegistry.length - 1) {
var nextRow = rulesRegistry[idx + 1].row;
row.fadeOut(150, function() {
row.insertAfter(nextRow).fadeIn(150).addClass('wa-highlight');
setTimeout(function() {
row.removeClass('wa-highlight');
}, 500);
});
arrayMove(rulesRegistry, idx, idx + 1);
updateRuleButtons();
}
});
updateRuleButtons();
}
btnAddRule.on('click', addRule);
addRule();
togWikiTypos.on('change', function(v) {
if (v) {
if (wikiTypos.length > 0) lblWikiStatus.text(wikiTypos.length + ' rules loaded (Cached)');
else {
lblWikiStatus.text('Fetching...');
togWikiTypos.setDisabled(true);
new mw.Api().get({
action: 'query',
prop: 'revisions',
titles: mw.config.get('wgFormattedNamespaces')[4] + ':AutoWikiBrowser/Typos',
rvprop: 'content',
rvslots: 'main',
formatversion: 2
}).then(function(d) {
var page = d.query.pages[0];
if (!page.missing) {
wikiTypos = parseTypoContent(page.revisions[0].slots.main.content);
lblWikiStatus.text(wikiTypos.length + ' rules loaded');
} else {
lblWikiStatus.text('Page not found');
togWikiTypos.setValue(false);
}
}).catch(function() {
lblWikiStatus.text('Error');
togWikiTypos.setValue(false);
}).always(function() {
togWikiTypos.setDisabled(false);
});
}
} else lblWikiStatus.text('Rules inactive');
});
btnLoadLocal.on('click', function() {
$typoInput.click();
});
$typoInput.on('change', function(e) {
var file = e.target.files[0];
if (!file) return;
var reader = new FileReader();
reader.onload = function(evt) {
localTypos = parseTypoContent(evt.target.result);
lblLocalStatus.text(localTypos.length + ' local rules loaded');
btnClearLocal.setDisabled(false);
};
reader.readAsText(file);
$typoInput.val('');
});
btnClearLocal.on('click', function() {
localTypos = [];
lblLocalStatus.text('No local rules');
btnClearLocal.setDisabled(true);
});
btnLoadLib.on('click', function() {
$libInput.click();
});
btnRemoveLib.on('click', function() {
currentLibrary = {
name: null,
code: null
};
updateLibUI();
});
$libInput.on('change', function(e) {
var file = e.target.files[0];
if (!file) return;
var reader = new FileReader();
reader.onload = function(evt) {
currentLibrary = {
name: file.name,
code: evt.target.result
};
updateLibUI();
};
reader.readAsText(file);
$libInput.val('');
});
btnPower.on('click', function() {
hasNewSources = false;
checkSummaryWarning();
if (!isRunning) {
if (SAVED_SESSION === 0) mw.track('stats.mediawiki_gadget_wAwB_total');
isRunning = true;
toggleUI(true);
loadNextPage();
} else {
if (SAVED_RUN > 0) {
mw.track('stats.mediawiki_gadget_wAwB_saved_total', SAVED_RUN, {
wiki: WIKI
});
SAVED_SESSION += SAVED_RUN;
SAVED_RUN = 0;
}
isRunning = false;
toggleUI(false);
WorkerEngine.destroy();
resetPanels();
}
});
inputSummary.on('change', function() {
checkSummaryWarning();
});
btnSkip.on('click', function() {
if (Editor.getValue() === 'Loading...') return;
removeTopLine();
loadNextPage();
});
btnDiff.on('click', function() {
currentViewMode = 'diff';
if (currentTitle) renderDiff();
});
btnPreview.on('click', function() {
currentViewMode = 'preview';
if (currentTitle) renderPreview();
});
btnSave.on('click', function() {
if (Editor.getValue() === 'Loading...' || !currentTitle) return;
if (autoSaveTimer) clearTimeout(autoSaveTimer);
btnSave.setDisabled(true);
setStatus('Saving...', 'working');
var effectiveDelay = PERMS.saveDelay || 0;
if (effectiveDelay > 0) setStatus('Throttling (' + (effectiveDelay / 1000) + 's)...', 'working');
setTimeout(function() {
if (effectiveDelay > 0) setStatus('Saving...', 'working');
var summary = inputSummary.getValue() + SUMMARY_SUFFIX;
new mw.Api().postWithToken('csrf', {
action: 'edit',
title: currentTitle,
text: Editor.getValue(),
summary: summary,
minor: chkMinor.isSelected(),
baserevid: baseRevId,
bot: PERMS.allowBot,
tags: DO_TAG ? APP_NAME : undefined
}).then(function() {
SAVED_RUN += 1;
removeTopLine();
loadNextPage();
}).catch(function(c) {
btnSave.setDisabled(false);
setStatus('Save error', 'error');
alert('Save failed: ' + c);
});
}, effectiveDelay);
});
btnWatch.on('click', function() {
var isWatched = btnWatch.getIcon() === 'unStar';
new mw.Api()[isWatched ? 'unwatch' : 'watch'](currentTitle).then(function() {
btnWatch.setIcon(isWatched ? 'star' : 'unStar');
mw.notify(isWatched ? 'Unwatched' : 'Watched');
});
});
btnAdd.on('click', function() {
if (isFetching) {
isFetching = false;
btnAdd.setDisabled(true).setLabel('Cancelling...');
return;
}
try {
var mode = srcSelect.getValue(),
q = srcInput.getValue().trim();
if (mode !== 'watchlist' && mode !== 'usercontribs' && mode !== 'pageswithprop' && !q) {
alert('Query empty');
return;
}
var nsIds = ($nsSelect.val() || []).map(v => parseInt(v));
var nsStr = nsIds.join('|');
var api = new mw.Api(),
promises = [];
if (mode === 'cat') promises.push(fetchWithContinue(api, {
action: 'query',
list: 'categorymembers',
cmtitle: mw.Title.newFromText(q, 14) ? mw.Title.newFromText(q, 14).getPrefixedText() : 'Category:' + q,
cmnamespace: nsStr,
cmtype: (chkCatPages.isSelected() ? 'page|' : '') + (chkCatSub.isSelected() ? 'subcat|' : '') + (chkCatFile.isSelected() ? 'file' : ''),
cmlimit: 'max'
}));
else if (mode === 'linksto') {
if (chkLinkWiki.isSelected()) promises.push(fetchWithContinue(api, {
action: 'query',
list: 'backlinks',
bltitle: q,
blnamespace: nsStr,
bllimit: 'max',
blfilterredir: dropLinkRedir.getValue(),
blredirect: chkLinkToRedir.isSelected()
}));
if (chkLinkTrans.isSelected()) promises.push(fetchWithContinue(api, {
action: 'query',
list: 'embeddedin',
eititle: q,
einamespace: nsStr,
eilimit: 'max',
eifilterredir: dropLinkRedir.getValue()
}));
if (chkLinkImg.isSelected()) promises.push(fetchWithContinue(api, {
action: 'query',
list: 'imageusage',
iutitle: q,
iunamespace: nsStr,
iulimit: 'max',
iufilterredir: dropLinkRedir.getValue()
}));
} else if (mode === 'linkson') promises.push(fetchWithContinue(api, {
action: 'query',
generator: 'links',
titles: q,
gplnamespace: nsStr,
gpllimit: 'max',
prop: 'info'
}));
else if (mode === 'prefix') promises.push(fetchWithContinue(api, {
action: 'query',
list: 'allpages',
apprefix: q,
apnamespace: nsIds[0] || 0,
aplimit: 'max'
}));
else if (mode === 'watchlist') promises.push(fetchWithContinue(api, {
action: 'query',
list: 'watchlistraw',
wrnamespace: nsStr,
wrlimit: 'max'
}));
else if (mode === 'search') promises.push(fetchWithContinue(api, {
action: 'query',
list: 'search',
srsearch: q,
srnamespace: nsStr,
srlimit: 'max'
}));
else if (mode === 'usercontribs') promises.push(fetchWithContinue(api, {
action: 'query',
list: 'usercontribs',
ucuser: srcInputUser.getValue(),
ucstart: srcInputStartDate.getValue(),
ucend: srcInputEndDate.getValue(),
ucdir: 'newer',
uclimit: 'max',
ucnamespace: nsStr,
ucprop: 'title'
}));
else if (mode === 'pageswithprop') promises.push(fetchWithContinue(api, {
action: 'query',
list: 'pageswithprop',
pwppropname: srcDropProp.getValue(),
pwplimit: 'max'
}));
Promise.all(promises).then(function(res) {
var list = new Set();
res.forEach(titles => titles.forEach(t => list.add(t)));
var currentVal = listTextarea.getValue();
var newVal = Array.from(list).join('\n');
listTextarea.setValue(currentVal ? currentVal + '\n' + newVal : newVal);
mw.notify('Added ' + list.size + ' pages');
}).catch(e => alert('Error: ' + e));
} catch (e) {
alert("Fetch error: " + e);
}
});
btnSort.on('click', function() {
var v = listTextarea.getValue();
if (v) {
var lines = getNormalizedList(v);
lines.sort((a, b) => sortAsc ? a.localeCompare(b) : b.localeCompare(a));
listTextarea.setValue(lines.join('\n'));
sortAsc = !sortAsc;
}
});
btnDedup.on('click', function() {
var v = listTextarea.getValue();
if (v) listTextarea.setValue(getDeduplicatedList(v).join('\n'));
});
btnClear.on('click', function() {
listTextarea.setValue('');
});
btnSaveProj.on('click', function() {
try {
var currentMode = srcSelect.getValue();
if (['watchlist', 'usercontribs', 'pageswithprop'].indexOf(currentMode) === -1) queryCache[currentMode] = srcInput.getValue();
var saveExcludes = {};
for (var k in protCheckboxes) saveExcludes[k] = protCheckboxes[k].isSelected();
var data = {
version: APP_VERSION,
library: currentLibrary,
source: {
activeMode: currentMode,
namespaces: ($nsSelect.val() || []).map(v => parseInt(v)),
modes: {
cat: {
query: queryCache['cat'] || '',
options: {
pages: chkCatPages.isSelected(),
sub: chkCatSub.isSelected(),
file: chkCatFile.isSelected()
}
},
linksto: {
query: queryCache['linksto'] || '',
options: {
wiki: chkLinkWiki.isSelected(),
trans: chkLinkTrans.isSelected(),
img: chkLinkImg.isSelected(),
redir: dropLinkRedir.getValue(),
toRedir: chkLinkToRedir.isSelected()
}
},
linkson: {
query: queryCache['linkson'] || ''
},
prefix: {
query: queryCache['prefix'] || ''
},
watchlist: {
query: ''
},
search: {
query: queryCache['search'] || ''
},
usercontribs: {
options: {
user: srcInputUser.getValue(),
start: srcInputStartDate.getValue(),
end: srcInputEndDate.getValue()
}
},
pageswithprop: {
options: {
prop: srcDropProp.getValue()
}
}
}
},
settings: {
redir: redirMode.findSelectedItem().getData(),
skipLogic: radSkipExist.findSelectedItem().getData(),
skipNoChange: chkSkipNoChange.isSelected(),
minor: chkMinor.isSelected()
},
filters: {
contains: {
val: inpSkipContains.getValue(),
regex: togSkipContainsRegex.getValue()
},
notContains: {
val: inpSkipNotContains.getValue(),
regex: togSkipNotContainsRegex.getValue()
},
categories: {
skip: inpSkipCategories.getValue(),
require: inpSkipNotCategories.getValue()
}
},
rules: rulesRegistry.map(r => ({
find: r.find.getValue(),
replace: r.rep.getValue(),
regex: r.regex.getValue(),
flags: r.flags.getValue(),
enabled: r.isActive(),
isFunc: r.isFunc()
})),
scripts: {
pre: txtPreScript.getValue(),
post: txtPostScript.getValue()
},
processing: {
summary: inputSummary.getValue(),
list: listTextarea.getValue()
},
protection: {
mode: dropProtMode.getValue(),
excludes: saveExcludes,
target: (radTargetSet.findSelectedItem() || {
getData: () => null
}).getData(),
templateFilter: inpTemplateFilter.getValue()
}
};
var a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([JSON.stringify(data, null, 1)], {
type: "application/json"
}));
a.download = "wawb_project.json";
a.click();
} catch (e) {
alert("Save error: " + e);
}
});
btnLoadProj.on('click', function() {
$fileInput.click();
});
$fileInput.on('change', function(e) {
var file = e.target.files[0];
if (!file) return;
var reader = new FileReader();
reader.onload = function(evt) {
try {
var data = JSON.parse(evt.target.result);
isLoadingProject = true;
if (data.source) {
if (data.source.namespaces) $nsSelect.val(data.source.namespaces.map(String));
if (data.source.modes) {
var m = data.source.modes;
queryCache = {};
for (var key in m)
if (m[key].query !== undefined) queryCache[key] = m[key].query;
if (m.cat && m.cat.options) {
chkCatPages.setSelected(m.cat.options.pages);
chkCatSub.setSelected(m.cat.options.sub);
chkCatFile.setSelected(m.cat.options.file);
}
if (m.linksto && m.linksto.options) {
chkLinkWiki.setSelected(m.linksto.options.wiki);
chkLinkTrans.setSelected(m.linksto.options.trans);
chkLinkImg.setSelected(m.linksto.options.img);
dropLinkRedir.setValue(m.linksto.options.redir);
chkLinkToRedir.setSelected(m.linksto.options.toRedir);
}
if (m.usercontribs && m.usercontribs.options) {
srcInputUser.setValue(m.usercontribs.options.user);
srcInputStartDate.setValue(m.usercontribs.options.start);
srcInputEndDate.setValue(m.usercontribs.options.end);
}
if (m.pageswithprop && m.pageswithprop.options) srcDropProp.setValue(m.pageswithprop.options.prop);
}
if (data.source.activeMode) {
srcSelect.setValue(data.source.activeMode);
isLoadingProject = false;
srcSelect.emit('change', data.source.activeMode);
isLoadingProject = true;
}
}
if (data.settings) {
redirMode.selectItemByData(data.settings.redir);
chkSkipNoChange.setSelected(data.settings.skipNoChange);
radSkipExist.selectItemByData(data.settings.skipLogic);
if (data.settings.minor !== undefined) chkMinor.setSelected(data.settings.minor);
}
if (data.protection) {
dropProtMode.setValue(data.protection.mode);
for (var k in data.protection.excludes)
if (protCheckboxes[k]) protCheckboxes[k].setSelected(data.protection.excludes[k]);
if (data.protection.target) radTargetSet.selectItemByData(data.protection.target);
if (data.protection.templateFilter) inpTemplateFilter.setValue(data.protection.templateFilter);
}
if (data.library) {
currentLibrary = data.library;
updateLibUI();
}
if (data.filters) {
inpSkipContains.setValue(data.filters.contains.val);
togSkipContainsRegex.setValue(data.filters.contains.regex);
inpSkipNotContains.setValue(data.filters.notContains.val);
togSkipNotContainsRegex.setValue(data.filters.notContains.regex);
}
if (data.filters.categories) {
inpSkipCategories.setValue(data.filters.categories.skip);
inpSkipNotCategories.setValue(data.filters.categories.require);
}
rulesRegistry.forEach(r => r.row.remove());
rulesRegistry = [];
$rulesList.empty();
if (data.rules) data.rules.forEach(r => {
addRule();
var last = rulesRegistry[rulesRegistry.length - 1];
last.find.setValue(r.find);
last.rep.setValue(r.replace);
last.regex.setValue(r.regex);
last.flags.setValue(r.flags);
last.flags.setDisabled(!r.regex);
last.setActive(r.enabled);
if (r.isFunc) last.setFunc(true);
});
if (rulesRegistry.length === 0) addRule();
if (data.scripts) {
txtPreScript.setValue(data.scripts.pre);
txtPostScript.setValue(data.scripts.post);
}
if (data.processing) {
inputSummary.setValue(data.processing.summary);
listTextarea.setValue(data.processing.list);
}
isLoadingProject = false;
setStatus('Project loaded');
} catch (err) {
alert("Error: " + err);
}
$fileInput.val('');
};
reader.readAsText(file);
});
if (CAN_MOVE || IS_ADMIN) {
togAdminEnable.on('change', function(val) {
if (!currentTitle) {
updateInterfaceMode();
return;
}
if (val) {
Editor.setValue(originalWikitext);
updateInterfaceMode();
renderDiff();
setStatus('Ready (Page actions)');
} else processPageContent();
});
}
if (CAN_MOVE) {
btnAdminMove.on('click', function() {
if (!currentVars['$xA']) {
mw.notify('Variable $xA not set', {
type: 'error'
});
return;
}
new mw.Api().postWithToken('csrf', {
action: 'move',
from: currentTitle,
to: currentVars['$xA'],
reason: inputSummary.getValue() + SUMMARY_SUFFIX,
movetalk: chkMovTalk.isSelected(),
movesubpages: chkMovSub.isSelected(),
noredirect: chkMovRedirect.isSelected()
}).then(function() {
removeTopLine();
loadNextPage();
}).catch(e => alert('Move failed: ' + e));
});
}
if (IS_ADMIN) {
btnAdminDel.on('click', function() {
new mw.Api().postWithToken('csrf', {
action: 'delete',
title: currentTitle,
reason: inputSummary.getValue() + SUMMARY_SUFFIX
}).then(function() {
if (chkDelTalk.isSelected()) new mw.Api().postWithToken('csrf', {
action: 'delete',
title: mw.Title.newFromText(currentTitle).getTalkPage().getPrefixedText(),
reason: 'Talk page of deleted page'
});
removeTopLine();
loadNextPage();
}).catch(e => alert('Delete failed: ' + e));
});
btnAdminProt.on('click', function() {
var protections = [];
if (dropProtEdit.getValue()) protections.push('edit=' + dropProtEdit.getValue());
if (dropProtMove.getValue()) protections.push('move=' + dropProtMove.getValue());
new mw.Api().postWithToken('csrf', {
action: 'protect',
title: currentTitle,
protections: protections.join('|'),
expiry: inpProtExpiry.getValue() || 'infinite',
reason: inputSummary.getValue() + SUMMARY_SUFFIX
}).then(function() {
removeTopLine();
loadNextPage();
}).catch(e => alert('Protect failed: ' + e));
});
}
Editor.init();
resetPanels();
});
$(window).on('beforeunload', function() {
return "You have unsaved work.";
});
}).catch(e => console.error("wAwB Loader Error:", e));
//</nowiki>
i16q8vtkk7a3dly5y4hjetvm1w32wjq
Vikipediya:Addəyişmə müraciətləri
0
174535
739018
735469
2026-04-21T13:50:43Z
~2026-23062-17
73557
showcaptcha
739018
wikitext
text/x-wiki
{{subst:yeni addəyişmə müraciəti|1=Azala|2=asdfjasdjfjasdjfdsaj|3=mirta [[User:Əkrəm|<span style="padding: 4px 6px; background-color: black; color: white; font-weight: bold">əkrəm<span style="color: #ABABAB">.</span></span>]] 23:31, 27 March 2026 (UTC)}}
{{subst:Vikipediya:Addəyişmə müraciətləri/Yeni müraciət|1=Avoidant personality disorder|2=mdasfmdsmfasmd|3=mırta [[User:Əkrəm|<span style="padding: 4px 6px; background-color: black; color: white; font-weight: bold">əkrəm<span style="color: #ABABAB">.</span></span>]] 00:54, 28 March 2026 (UTC)}}
{{subst:Vikipediya:Addəyişmə müraciətləri/Yeni müraciət |1= Azala |2= asdfasdfdasfas|3= nəbilime [[User:Əkrəm|<span style="padding: 4px 6px; background-color: black; color: white; font-weight: bold">əkrəm<span style="color: #ABABAB">.</span></span>]] 03:38, 28 March 2026 (UTC) }}
this is an edit
ardaz7b9pkuipogmp1e5fe2hgrmm9se
User:Axwvwxz/Pywikibot Test
2
174956
739021
2026-04-21T15:06:14Z
Axwvwxz
73360
Creating a test subpage
739021
wikitext
text/x-wiki
This is a brand new page created via Termux and Pywikibot!
f7n43cava8pp6k8gmu08j8n74aa0lrn
739025
739021
2026-04-21T15:19:34Z
Axwvwxz
73360
Batch update using PageGenerator
739025
wikitext
text/x-wiki
This is a brand new page created via Termux and Pywikibot!
Updated by Auto-Pilot Mode 🚀
gswueu167r2u6ohdb1gch6iqoj2gsnh
User:Axwvwxz/Test Page 1
2
174957
739022
2026-04-21T15:15:00Z
Axwvwxz
73360
Testing mass edit on 3 pages
739022
wikitext
text/x-wiki
This is an automated message on User:Axwvwxz/Test_Page_1.
Status: Level 2 Automation Successful!
j63bvs0hphrdj10xdo6p9yv0not9bvz
739026
739022
2026-04-21T15:19:44Z
Axwvwxz
73360
Batch update using PageGenerator
739026
wikitext
text/x-wiki
This is an automated message on User:Axwvwxz/Test_Page_1.
Status: Level 2 Automation Successful!
Updated by Auto-Pilot Mode 🚀
on834jeuyt5carbxf3atoz7xuog3128
User:Axwvwxz/Test Page 2
2
174958
739023
2026-04-21T15:15:10Z
Axwvwxz
73360
Testing mass edit on 3 pages
739023
wikitext
text/x-wiki
This is an automated message on User:Axwvwxz/Test_Page_2.
Status: Level 2 Automation Successful!
dfxhakdflrgklwc0kdcee6ui8z7m207
739027
739023
2026-04-21T15:19:54Z
Axwvwxz
73360
Batch update using PageGenerator
739027
wikitext
text/x-wiki
This is an automated message on User:Axwvwxz/Test_Page_2.
Status: Level 2 Automation Successful!
Updated by Auto-Pilot Mode 🚀
f0e3kcrjqvuj2bryh2ns5pfpp27duwr
User:Axwvwxz/Test Page 3
2
174959
739024
2026-04-21T15:15:20Z
Axwvwxz
73360
Testing mass edit on 3 pages
739024
wikitext
text/x-wiki
This is an automated message on User:Axwvwxz/Test_Page_3.
Status: Level 2 Automation Successful!
8e4ho54soybzi9dkvqrefa8h3asrdbj
739028
739024
2026-04-21T15:20:04Z
Axwvwxz
73360
Batch update using PageGenerator
739028
wikitext
text/x-wiki
This is an automated message on User:Axwvwxz/Test_Page_3.
Status: Level 2 Automation Successful!
Updated by Auto-Pilot Mode 🚀
eokdunsybmh058ew23ic41bn9wrd01n
.gb
0
174960
739036
2026-04-21T15:58:06Z
MPostoronca-WMF
72719
test
739036
wikitext
text/x-wiki
testing source editor on mobile
ek8idou1kcb26jn0ep22xtqtl0l42ne
Template:Autoarchive
10
174963
739050
2026-04-21T23:29:03Z
AsteraBot
69554
Let's hope this works
739050
wikitext
text/x-wiki
<includeonly>
{{{archive|}}}{{{ago|}}}
</includeonly>
d342moc1wia7la600qvkpwhetf37ukt
739051
739050
2026-04-21T23:46:39Z
AsteraBot
69554
fix
739051
wikitext
text/x-wiki
{{{archive|}}}{{{ago|}}}
1tipnph5730ibnjsg1qbo0bphptel02
739053
739051
2026-04-22T00:12:57Z
AsteraBot
69554
Fix??
739053
wikitext
text/x-wiki
<includeonly><!-- AUTOARCHIVE CONFIG -->
{{{archive|}}}||{{{ago|}}}
</includeonly>
6wz0uywc28vsqmydzuvxwtdxmfbeh77
739055
739053
2026-04-22T00:19:06Z
AsteraBot
69554
There's only one way left
739055
wikitext
text/x-wiki
<includeonly>
<!-- Autoarchive config: intentionally empty -->
</includeonly>
e2rv7bpvr5g5rv0zhqxrrzm0h2ahfaj
User talk:AsteraBot
3
174964
739058
2026-04-22T00:27:03Z
AsteraBot
69554
Lazy copy-paste testing
739058
wikitext
text/x-wiki
{{Autoarchive
|archive=%(fullpage)/Archives/%(year)
|ago=2m
}}
== Justarandomamerican (Steward)==
<div class="boilerplate metadata discussion-archived" style="background-color: #F2F4FC; margin: 2em 0 0 0; padding: 0 10px 0 10px; border: 1px solid #aaa">
:''The following discussion is closed. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.''
::With the unanimous support of 9 community members, this request is '''successful'''. Congratulations on being the first officially elected steward and welcome (back?) to the team! :) [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 21:10, 21 April 2026 (UTC)
----
Hello! I'm Justarandomamerican, and I am filing a request for stewardship, because the board has abolished the staff role, and for staff members to keep their permissions, they must stand for election. I've been here since 2024, when the farm was founded. I hope the community trusts me. Thank you for your time! [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 21:10, 16 April 2026 (UTC)
:<small> This candidate has signed an NDA with the WikiOasis Foundation, as is required for stewardship. [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 01:23, 17 April 2026 (UTC)</small>
=== Questions ===
In the interests of making this more of an interesting process than a formality, I have a few questions which more or less mirror to all candidates. Some I admit are bundled questions, feel free to answer to the extent applicable.
# Based on your request, what do you currently do that requires Steward capabilities?
# Do you believe you have made mistakes in your official capacity on this platform so far? If so, what did you do to improve from those mistakes? You could elaborate on just one if you like, or a couple.
# What is your vision of what a steward is, and what they do for the community?
# Two users appear in the CheckUser interface who both appear to be using Tor who are regular editors on a large wiki, which is also being attacked seemingly out of nowhere by an assailant who is using Tor and free VPNs. Is this a slam dunk connection, if your review of edits suggest their editing styles are similar (but no major stylistic tells)? Why/why not, and if not, what's the next step to find out?
# How would you close a vote proposing a change in which seven people essentially say support and maybe a few words, nothing really in depth, and three people have articulated major concerns with the proposal at the level of detail of these questions? Would that process change if it was only one oppose like this to seven support, and if that oppose appeared just before you were going to close the RfC?
Thank you, --'''[[User:Raidarr|raidarr]]''' '''('''[[User_talk:Raidarr|📡]]''')''' 22:45, 17 April 2026 (UTC)
:Hello! Sorry I was unable to respond; I have other things going on.
:# CheckUser and Oversight, definitely; I need those to help with abuse and help our wikis out.
:# Oh yes, definitely. I have made mistakes, including unauthorized usage of Foundation funds. I have been working on impulse control, and will be in therapy for the foreseeable future. Thank you for asking.
:# A steward is, fundamentally, a community representative. I represent the community, even though I might have some control over it, I shouldn't use it in an authoritarian manner.
:# This does not seem to be a slam dunk connection. I would keep watching their contribs to find out.
:# First one, would close as unsuccessful, it is not a consensus to have several support votes with no words, and a few major concerns articulated in detail. Second one, would relist. All opinions should have a chance to be considered, no matter how late they appeared.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:27, 20 April 2026 (UTC)
A few questions targeted towards individual candidates across the process:
# How do you see your history as president of the WikiOasis Foundation fits into this role, and the trust that the community will be placing in you that may not yet have been rebuilt?
# What do you think that you would bring to the steward team that other candidates may not?
# Can you discuss what your reasoning behind [https://italianbrainrot.wikioasis.org/wiki/Special:Log?logid=23008 this] was, as there were local administrators at the time who could've handled this apparent one off incident?
# How do you feel that team discussion fits into the handling of appeals by banned and locked users, do you think that an appeal should be solely handled by one person without discussion?
Best, --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 00:14, 18 April 2026 (UTC)
:# Honestly, I see it fitting into this role somewhat. It was a major breach of trust, and I have worked on myself since then. I just hope it never happens again, with anybody.
:# There are numerous experienced candidates, so nothing new there. I might just bring my track record of good behavior since the Foundation incident.
:# That was clear and obvious vandalism, which can be handled by anyone with rights to delete, including GPs and GAs.
:# I think there should be team discussion around appeals. One person handling it doesn't seem good, as they may be biased.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:32, 20 April 2026 (UTC)
=== Discussion ===
# {{support}}, has a clue, not a jerk --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 01:14, 17 April 2026 (UTC)
#{{Support}} [[User:SleepingElephant2145|SleepingElephant2145]] ([[User talk:SleepingElephant2145|talk]]) 01:20, 17 April 2026 (UTC)
# I have noticed Justarandomamerican around testwiki.wiki (where they serve as system admin/steward) and can {{support}} this request. <span style="font-family:Courier New;font-weight:bold;text-shadow:1px 1px 1px cyan">[[User:Tester|Tester]]</span> ([[User_talk:Tester|ᴛ]]•[[Special:Contributions/Tester|ᴄ]]) 01:40, 17 April 2026 (UTC)
# {{Support|strongest}} <span style="background:#EFD8FD;color:Indigo;font-family:serif">~ '''''[[User:Dream Indigo|<span style="color:Indigo">Dream Indigo</span>]]''''' [[User talk:Dream Indigo|<span style="color:Indigo">✩</span>]]</span> 01:49, 17 April 2026 (UTC)
# No brainer {{support}}. [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 01:53, 17 April 2026 (UTC)
# Ex-Board Member and is trusted within the WikiOasis Volunteers. No brainer {{support}}. [[User:Fearless|Fearless]] ([[User talk:Fearless|talk]]) 10:22, 17 April 2026 (UTC)
# {{Support}} --[[User:PinkPugPrincess|PinkPugPrincess]] ([[User talk:PinkPugPrincess|talk]]) 13:36, 17 April 2026 (UTC)
# {{Support|Strongest}} [[User:AlPaD|AlPaD]] ([[User talk:AlPaD|talk]]) 14:52, 17 April 2026 (UTC)
# {{Support}} No Concerns --[[User:Cocopuff2018|Cocopuff2018]] ([[User talk:Cocopuff2018|talk]]) 16:36, 17 April 2026 (UTC)
----
:''The above discussion is preserved as an archive. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.'' </div>
qogasqzfdelv0ysbj7ir85jjiq1gr0x
739060
739058
2026-04-22T00:36:03Z
AsteraBot
69554
Bot: removing archived sections
739060
wikitext
text/x-wiki
{{Autoarchive
|archive=%(fullpage)/Archives/%(year)
|ago=2m
}}
=== Questions ===
In the interests of making this more of an interesting process than a formality, I have a few questions which more or less mirror to all candidates. Some I admit are bundled questions, feel free to answer to the extent applicable.
# Based on your request, what do you currently do that requires Steward capabilities?
# Do you believe you have made mistakes in your official capacity on this platform so far? If so, what did you do to improve from those mistakes? You could elaborate on just one if you like, or a couple.
# What is your vision of what a steward is, and what they do for the community?
# Two users appear in the CheckUser interface who both appear to be using Tor who are regular editors on a large wiki, which is also being attacked seemingly out of nowhere by an assailant who is using Tor and free VPNs. Is this a slam dunk connection, if your review of edits suggest their editing styles are similar (but no major stylistic tells)? Why/why not, and if not, what's the next step to find out?
# How would you close a vote proposing a change in which seven people essentially say support and maybe a few words, nothing really in depth, and three people have articulated major concerns with the proposal at the level of detail of these questions? Would that process change if it was only one oppose like this to seven support, and if that oppose appeared just before you were going to close the RfC?
Thank you, --'''[[User:Raidarr|raidarr]]''' '''('''[[User_talk:Raidarr|📡]]''')''' 22:45, 17 April 2026 (UTC)
:Hello! Sorry I was unable to respond; I have other things going on.
:# CheckUser and Oversight, definitely; I need those to help with abuse and help our wikis out.
:# Oh yes, definitely. I have made mistakes, including unauthorized usage of Foundation funds. I have been working on impulse control, and will be in therapy for the foreseeable future. Thank you for asking.
:# A steward is, fundamentally, a community representative. I represent the community, even though I might have some control over it, I shouldn't use it in an authoritarian manner.
:# This does not seem to be a slam dunk connection. I would keep watching their contribs to find out.
:# First one, would close as unsuccessful, it is not a consensus to have several support votes with no words, and a few major concerns articulated in detail. Second one, would relist. All opinions should have a chance to be considered, no matter how late they appeared.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:27, 20 April 2026 (UTC)
A few questions targeted towards individual candidates across the process:
# How do you see your history as president of the WikiOasis Foundation fits into this role, and the trust that the community will be placing in you that may not yet have been rebuilt?
# What do you think that you would bring to the steward team that other candidates may not?
# Can you discuss what your reasoning behind [https://italianbrainrot.wikioasis.org/wiki/Special:Log?logid=23008 this] was, as there were local administrators at the time who could've handled this apparent one off incident?
# How do you feel that team discussion fits into the handling of appeals by banned and locked users, do you think that an appeal should be solely handled by one person without discussion?
Best, --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 00:14, 18 April 2026 (UTC)
:# Honestly, I see it fitting into this role somewhat. It was a major breach of trust, and I have worked on myself since then. I just hope it never happens again, with anybody.
:# There are numerous experienced candidates, so nothing new there. I might just bring my track record of good behavior since the Foundation incident.
:# That was clear and obvious vandalism, which can be handled by anyone with rights to delete, including GPs and GAs.
:# I think there should be team discussion around appeals. One person handling it doesn't seem good, as they may be biased.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:32, 20 April 2026 (UTC)
=== Discussion ===
# {{support}}, has a clue, not a jerk --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 01:14, 17 April 2026 (UTC)
#{{Support}} [[User:SleepingElephant2145|SleepingElephant2145]] ([[User talk:SleepingElephant2145|talk]]) 01:20, 17 April 2026 (UTC)
# I have noticed Justarandomamerican around testwiki.wiki (where they serve as system admin/steward) and can {{support}} this request. <span style="font-family:Courier New;font-weight:bold;text-shadow:1px 1px 1px cyan">[[User:Tester|Tester]]</span> ([[User_talk:Tester|ᴛ]]•[[Special:Contributions/Tester|ᴄ]]) 01:40, 17 April 2026 (UTC)
# {{Support|strongest}} <span style="background:#EFD8FD;color:Indigo;font-family:serif">~ '''''[[User:Dream Indigo|<span style="color:Indigo">Dream Indigo</span>]]''''' [[User talk:Dream Indigo|<span style="color:Indigo">✩</span>]]</span> 01:49, 17 April 2026 (UTC)
# No brainer {{support}}. [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 01:53, 17 April 2026 (UTC)
# Ex-Board Member and is trusted within the WikiOasis Volunteers. No brainer {{support}}. [[User:Fearless|Fearless]] ([[User talk:Fearless|talk]]) 10:22, 17 April 2026 (UTC)
# {{Support}} --[[User:PinkPugPrincess|PinkPugPrincess]] ([[User talk:PinkPugPrincess|talk]]) 13:36, 17 April 2026 (UTC)
# {{Support|Strongest}} [[User:AlPaD|AlPaD]] ([[User talk:AlPaD|talk]]) 14:52, 17 April 2026 (UTC)
# {{Support}} No Concerns --[[User:Cocopuff2018|Cocopuff2018]] ([[User talk:Cocopuff2018|talk]]) 16:36, 17 April 2026 (UTC)
----
:''The above discussion is preserved as an archive. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.'' </div>
kvmaed6ahkvknqgxhv92vzcwe5rtoil
739061
739060
2026-04-22T00:48:53Z
AsteraBot
69554
Retrying
739061
wikitext
text/x-wiki
{{Autoarchive
|archive=%(fullpage)/Archives/%(year)
|ago=2m
}}
== Justarandomamerican (Steward)==
<div class="boilerplate metadata discussion-archived" style="background-color: #F2F4FC; margin: 2em 0 0 0; padding: 0 10px 0 10px; border: 1px solid #aaa">
:''The following discussion is closed. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.''
::With the unanimous support of 9 community members, this request is '''successful'''. Congratulations on being the first officially elected steward and welcome (back?) to the team! :) [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 21:10, 21 April 2026 (UTC)
----
Hello! I'm Justarandomamerican, and I am filing a request for stewardship, because the board has abolished the staff role, and for staff members to keep their permissions, they must stand for election. I've been here since 2024, when the farm was founded. I hope the community trusts me. Thank you for your time! [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 21:10, 16 April 2026 (UTC)
:<small> This candidate has signed an NDA with the WikiOasis Foundation, as is required for stewardship. [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 01:23, 17 April 2026 (UTC)</small>
=== Questions ===
In the interests of making this more of an interesting process than a formality, I have a few questions which more or less mirror to all candidates. Some I admit are bundled questions, feel free to answer to the extent applicable.
# Based on your request, what do you currently do that requires Steward capabilities?
# Do you believe you have made mistakes in your official capacity on this platform so far? If so, what did you do to improve from those mistakes? You could elaborate on just one if you like, or a couple.
# What is your vision of what a steward is, and what they do for the community?
# Two users appear in the CheckUser interface who both appear to be using Tor who are regular editors on a large wiki, which is also being attacked seemingly out of nowhere by an assailant who is using Tor and free VPNs. Is this a slam dunk connection, if your review of edits suggest their editing styles are similar (but no major stylistic tells)? Why/why not, and if not, what's the next step to find out?
# How would you close a vote proposing a change in which seven people essentially say support and maybe a few words, nothing really in depth, and three people have articulated major concerns with the proposal at the level of detail of these questions? Would that process change if it was only one oppose like this to seven support, and if that oppose appeared just before you were going to close the RfC?
Thank you, --'''[[User:Raidarr|raidarr]]''' '''('''[[User_talk:Raidarr|📡]]''')''' 22:45, 17 April 2026 (UTC)
:Hello! Sorry I was unable to respond; I have other things going on.
:# CheckUser and Oversight, definitely; I need those to help with abuse and help our wikis out.
:# Oh yes, definitely. I have made mistakes, including unauthorized usage of Foundation funds. I have been working on impulse control, and will be in therapy for the foreseeable future. Thank you for asking.
:# A steward is, fundamentally, a community representative. I represent the community, even though I might have some control over it, I shouldn't use it in an authoritarian manner.
:# This does not seem to be a slam dunk connection. I would keep watching their contribs to find out.
:# First one, would close as unsuccessful, it is not a consensus to have several support votes with no words, and a few major concerns articulated in detail. Second one, would relist. All opinions should have a chance to be considered, no matter how late they appeared.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:27, 20 April 2026 (UTC)
A few questions targeted towards individual candidates across the process:
# How do you see your history as president of the WikiOasis Foundation fits into this role, and the trust that the community will be placing in you that may not yet have been rebuilt?
# What do you think that you would bring to the steward team that other candidates may not?
# Can you discuss what your reasoning behind [https://italianbrainrot.wikioasis.org/wiki/Special:Log?logid=23008 this] was, as there were local administrators at the time who could've handled this apparent one off incident?
# How do you feel that team discussion fits into the handling of appeals by banned and locked users, do you think that an appeal should be solely handled by one person without discussion?
Best, --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 00:14, 18 April 2026 (UTC)
:# Honestly, I see it fitting into this role somewhat. It was a major breach of trust, and I have worked on myself since then. I just hope it never happens again, with anybody.
:# There are numerous experienced candidates, so nothing new there. I might just bring my track record of good behavior since the Foundation incident.
:# That was clear and obvious vandalism, which can be handled by anyone with rights to delete, including GPs and GAs.
:# I think there should be team discussion around appeals. One person handling it doesn't seem good, as they may be biased.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:32, 20 April 2026 (UTC)
=== Discussion ===
# {{support}}, has a clue, not a jerk --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 01:14, 17 April 2026 (UTC)
#{{Support}} [[User:SleepingElephant2145|SleepingElephant2145]] ([[User talk:SleepingElephant2145|talk]]) 01:20, 17 April 2026 (UTC)
# I have noticed Justarandomamerican around testwiki.wiki (where they serve as system admin/steward) and can {{support}} this request. <span style="font-family:Courier New;font-weight:bold;text-shadow:1px 1px 1px cyan">[[User:Tester|Tester]]</span> ([[User_talk:Tester|ᴛ]]•[[Special:Contributions/Tester|ᴄ]]) 01:40, 17 April 2026 (UTC)
# {{Support|strongest}} <span style="background:#EFD8FD;color:Indigo;font-family:serif">~ '''''[[User:Dream Indigo|<span style="color:Indigo">Dream Indigo</span>]]''''' [[User talk:Dream Indigo|<span style="color:Indigo">✩</span>]]</span> 01:49, 17 April 2026 (UTC)
# No brainer {{support}}. [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 01:53, 17 April 2026 (UTC)
# Ex-Board Member and is trusted within the WikiOasis Volunteers. No brainer {{support}}. [[User:Fearless|Fearless]] ([[User talk:Fearless|talk]]) 10:22, 17 April 2026 (UTC)
# {{Support}} --[[User:PinkPugPrincess|PinkPugPrincess]] ([[User talk:PinkPugPrincess|talk]]) 13:36, 17 April 2026 (UTC)
# {{Support|Strongest}} [[User:AlPaD|AlPaD]] ([[User talk:AlPaD|talk]]) 14:52, 17 April 2026 (UTC)
# {{Support}} No Concerns --[[User:Cocopuff2018|Cocopuff2018]] ([[User talk:Cocopuff2018|talk]]) 16:36, 17 April 2026 (UTC)
----
:''The above discussion is preserved as an archive. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.'' </div>
qogasqzfdelv0ysbj7ir85jjiq1gr0x
739064
739061
2026-04-22T00:54:01Z
AsteraBot
69554
Bot: removing archived sections
739064
wikitext
text/x-wiki
{{Autoarchive
|archive=%(fullpage)/Archives/%(year)
|ago=2m
}}
== Justarandomamerican (Steward)==
g1pgicnojfch2hncaptm04sm3cg8sjx
739065
739064
2026-04-22T00:56:15Z
AsteraBot
69554
Trying again
739065
wikitext
text/x-wiki
{{Autoarchive
|archive=%(fullpage)/Archives/%(year)
|ago=2m
}}
== Justarandomamerican (Steward)==
<div class="boilerplate metadata discussion-archived" style="background-color: #F2F4FC; margin: 2em 0 0 0; padding: 0 10px 0 10px; border: 1px solid #aaa">
:''The following discussion is closed. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.''
::With the unanimous support of 9 community members, this request is '''successful'''. Congratulations on being the first officially elected steward and welcome (back?) to the team! :) [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 21:10, 21 April 2026 (UTC)
----
Hello! I'm Justarandomamerican, and I am filing a request for stewardship, because the board has abolished the staff role, and for staff members to keep their permissions, they must stand for election. I've been here since 2024, when the farm was founded. I hope the community trusts me. Thank you for your time! [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 21:10, 16 April 2026 (UTC)
:<small> This candidate has signed an NDA with the WikiOasis Foundation, as is required for stewardship. [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 01:23, 17 April 2026 (UTC)</small>
=== Questions ===
In the interests of making this more of an interesting process than a formality, I have a few questions which more or less mirror to all candidates. Some I admit are bundled questions, feel free to answer to the extent applicable.
# Based on your request, what do you currently do that requires Steward capabilities?
# Do you believe you have made mistakes in your official capacity on this platform so far? If so, what did you do to improve from those mistakes? You could elaborate on just one if you like, or a couple.
# What is your vision of what a steward is, and what they do for the community?
# Two users appear in the CheckUser interface who both appear to be using Tor who are regular editors on a large wiki, which is also being attacked seemingly out of nowhere by an assailant who is using Tor and free VPNs. Is this a slam dunk connection, if your review of edits suggest their editing styles are similar (but no major stylistic tells)? Why/why not, and if not, what's the next step to find out?
# How would you close a vote proposing a change in which seven people essentially say support and maybe a few words, nothing really in depth, and three people have articulated major concerns with the proposal at the level of detail of these questions? Would that process change if it was only one oppose like this to seven support, and if that oppose appeared just before you were going to close the RfC?
Thank you, --'''[[User:Raidarr|raidarr]]''' '''('''[[User_talk:Raidarr|📡]]''')''' 22:45, 17 April 2026 (UTC)
:Hello! Sorry I was unable to respond; I have other things going on.
:# CheckUser and Oversight, definitely; I need those to help with abuse and help our wikis out.
:# Oh yes, definitely. I have made mistakes, including unauthorized usage of Foundation funds. I have been working on impulse control, and will be in therapy for the foreseeable future. Thank you for asking.
:# A steward is, fundamentally, a community representative. I represent the community, even though I might have some control over it, I shouldn't use it in an authoritarian manner.
:# This does not seem to be a slam dunk connection. I would keep watching their contribs to find out.
:# First one, would close as unsuccessful, it is not a consensus to have several support votes with no words, and a few major concerns articulated in detail. Second one, would relist. All opinions should have a chance to be considered, no matter how late they appeared.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:27, 20 April 2026 (UTC)
A few questions targeted towards individual candidates across the process:
# How do you see your history as president of the WikiOasis Foundation fits into this role, and the trust that the community will be placing in you that may not yet have been rebuilt?
# What do you think that you would bring to the steward team that other candidates may not?
# Can you discuss what your reasoning behind [https://italianbrainrot.wikioasis.org/wiki/Special:Log?logid=23008 this] was, as there were local administrators at the time who could've handled this apparent one off incident?
# How do you feel that team discussion fits into the handling of appeals by banned and locked users, do you think that an appeal should be solely handled by one person without discussion?
Best, --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 00:14, 18 April 2026 (UTC)
:# Honestly, I see it fitting into this role somewhat. It was a major breach of trust, and I have worked on myself since then. I just hope it never happens again, with anybody.
:# There are numerous experienced candidates, so nothing new there. I might just bring my track record of good behavior since the Foundation incident.
:# That was clear and obvious vandalism, which can be handled by anyone with rights to delete, including GPs and GAs.
:# I think there should be team discussion around appeals. One person handling it doesn't seem good, as they may be biased.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:32, 20 April 2026 (UTC)
=== Discussion ===
# {{support}}, has a clue, not a jerk --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 01:14, 17 April 2026 (UTC)
#{{Support}} [[User:SleepingElephant2145|SleepingElephant2145]] ([[User talk:SleepingElephant2145|talk]]) 01:20, 17 April 2026 (UTC)
# I have noticed Justarandomamerican around testwiki.wiki (where they serve as system admin/steward) and can {{support}} this request. <span style="font-family:Courier New;font-weight:bold;text-shadow:1px 1px 1px cyan">[[User:Tester|Tester]]</span> ([[User_talk:Tester|ᴛ]]•[[Special:Contributions/Tester|ᴄ]]) 01:40, 17 April 2026 (UTC)
# {{Support|strongest}} <span style="background:#EFD8FD;color:Indigo;font-family:serif">~ '''''[[User:Dream Indigo|<span style="color:Indigo">Dream Indigo</span>]]''''' [[User talk:Dream Indigo|<span style="color:Indigo">✩</span>]]</span> 01:49, 17 April 2026 (UTC)
# No brainer {{support}}. [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 01:53, 17 April 2026 (UTC)
# Ex-Board Member and is trusted within the WikiOasis Volunteers. No brainer {{support}}. [[User:Fearless|Fearless]] ([[User talk:Fearless|talk]]) 10:22, 17 April 2026 (UTC)
# {{Support}} --[[User:PinkPugPrincess|PinkPugPrincess]] ([[User talk:PinkPugPrincess|talk]]) 13:36, 17 April 2026 (UTC)
# {{Support|Strongest}} [[User:AlPaD|AlPaD]] ([[User talk:AlPaD|talk]]) 14:52, 17 April 2026 (UTC)
# {{Support}} No Concerns --[[User:Cocopuff2018|Cocopuff2018]] ([[User talk:Cocopuff2018|talk]]) 16:36, 17 April 2026 (UTC)
----
:''The above discussion is preserved as an archive. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.'' </div>
qogasqzfdelv0ysbj7ir85jjiq1gr0x
739067
739065
2026-04-22T00:56:58Z
AsteraBot
69554
Bot: removing archived sections
739067
wikitext
text/x-wiki
{{Autoarchive
|archive=%(fullpage)/Archives/%(year)
|ago=2m
}}
=== Questions ===
In the interests of making this more of an interesting process than a formality, I have a few questions which more or less mirror to all candidates. Some I admit are bundled questions, feel free to answer to the extent applicable.
# Based on your request, what do you currently do that requires Steward capabilities?
# Do you believe you have made mistakes in your official capacity on this platform so far? If so, what did you do to improve from those mistakes? You could elaborate on just one if you like, or a couple.
# What is your vision of what a steward is, and what they do for the community?
# Two users appear in the CheckUser interface who both appear to be using Tor who are regular editors on a large wiki, which is also being attacked seemingly out of nowhere by an assailant who is using Tor and free VPNs. Is this a slam dunk connection, if your review of edits suggest their editing styles are similar (but no major stylistic tells)? Why/why not, and if not, what's the next step to find out?
# How would you close a vote proposing a change in which seven people essentially say support and maybe a few words, nothing really in depth, and three people have articulated major concerns with the proposal at the level of detail of these questions? Would that process change if it was only one oppose like this to seven support, and if that oppose appeared just before you were going to close the RfC?
Thank you, --'''[[User:Raidarr|raidarr]]''' '''('''[[User_talk:Raidarr|📡]]''')''' 22:45, 17 April 2026 (UTC)
:Hello! Sorry I was unable to respond; I have other things going on.
:# CheckUser and Oversight, definitely; I need those to help with abuse and help our wikis out.
:# Oh yes, definitely. I have made mistakes, including unauthorized usage of Foundation funds. I have been working on impulse control, and will be in therapy for the foreseeable future. Thank you for asking.
:# A steward is, fundamentally, a community representative. I represent the community, even though I might have some control over it, I shouldn't use it in an authoritarian manner.
:# This does not seem to be a slam dunk connection. I would keep watching their contribs to find out.
:# First one, would close as unsuccessful, it is not a consensus to have several support votes with no words, and a few major concerns articulated in detail. Second one, would relist. All opinions should have a chance to be considered, no matter how late they appeared.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:27, 20 April 2026 (UTC)
A few questions targeted towards individual candidates across the process:
# How do you see your history as president of the WikiOasis Foundation fits into this role, and the trust that the community will be placing in you that may not yet have been rebuilt?
# What do you think that you would bring to the steward team that other candidates may not?
# Can you discuss what your reasoning behind [https://italianbrainrot.wikioasis.org/wiki/Special:Log?logid=23008 this] was, as there were local administrators at the time who could've handled this apparent one off incident?
# How do you feel that team discussion fits into the handling of appeals by banned and locked users, do you think that an appeal should be solely handled by one person without discussion?
Best, --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 00:14, 18 April 2026 (UTC)
:# Honestly, I see it fitting into this role somewhat. It was a major breach of trust, and I have worked on myself since then. I just hope it never happens again, with anybody.
:# There are numerous experienced candidates, so nothing new there. I might just bring my track record of good behavior since the Foundation incident.
:# That was clear and obvious vandalism, which can be handled by anyone with rights to delete, including GPs and GAs.
:# I think there should be team discussion around appeals. One person handling it doesn't seem good, as they may be biased.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:32, 20 April 2026 (UTC)
=== Discussion ===
# {{support}}, has a clue, not a jerk --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 01:14, 17 April 2026 (UTC)
#{{Support}} [[User:SleepingElephant2145|SleepingElephant2145]] ([[User talk:SleepingElephant2145|talk]]) 01:20, 17 April 2026 (UTC)
# I have noticed Justarandomamerican around testwiki.wiki (where they serve as system admin/steward) and can {{support}} this request. <span style="font-family:Courier New;font-weight:bold;text-shadow:1px 1px 1px cyan">[[User:Tester|Tester]]</span> ([[User_talk:Tester|ᴛ]]•[[Special:Contributions/Tester|ᴄ]]) 01:40, 17 April 2026 (UTC)
# {{Support|strongest}} <span style="background:#EFD8FD;color:Indigo;font-family:serif">~ '''''[[User:Dream Indigo|<span style="color:Indigo">Dream Indigo</span>]]''''' [[User talk:Dream Indigo|<span style="color:Indigo">✩</span>]]</span> 01:49, 17 April 2026 (UTC)
# No brainer {{support}}. [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 01:53, 17 April 2026 (UTC)
# Ex-Board Member and is trusted within the WikiOasis Volunteers. No brainer {{support}}. [[User:Fearless|Fearless]] ([[User talk:Fearless|talk]]) 10:22, 17 April 2026 (UTC)
# {{Support}} --[[User:PinkPugPrincess|PinkPugPrincess]] ([[User talk:PinkPugPrincess|talk]]) 13:36, 17 April 2026 (UTC)
# {{Support|Strongest}} [[User:AlPaD|AlPaD]] ([[User talk:AlPaD|talk]]) 14:52, 17 April 2026 (UTC)
# {{Support}} No Concerns --[[User:Cocopuff2018|Cocopuff2018]] ([[User talk:Cocopuff2018|talk]]) 16:36, 17 April 2026 (UTC)
----
:''The above discussion is preserved as an archive. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.'' </div>
kvmaed6ahkvknqgxhv92vzcwe5rtoil
User talk:AsteraBot/Archives/2026
3
174965
739059
2026-04-22T00:36:03Z
AsteraBot
69554
Bot: archiving sections
739059
wikitext
text/x-wiki
== Justarandomamerican (Steward)==
<div class="boilerplate metadata discussion-archived" style="background-color: #F2F4FC; margin: 2em 0 0 0; padding: 0 10px 0 10px; border: 1px solid #aaa">
:''The following discussion is closed. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.''
::With the unanimous support of 9 community members, this request is '''successful'''. Congratulations on being the first officially elected steward and welcome (back?) to the team! :) [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 21:10, 21 April 2026 (UTC)
----
Hello! I'm Justarandomamerican, and I am filing a request for stewardship, because the board has abolished the staff role, and for staff members to keep their permissions, they must stand for election. I've been here since 2024, when the farm was founded. I hope the community trusts me. Thank you for your time! [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 21:10, 16 April 2026 (UTC)
:<small> This candidate has signed an NDA with the WikiOasis Foundation, as is required for stewardship. [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 01:23, 17 April 2026 (UTC)</small>
5w3n0q97eh6hx1pcaxm25dkhodg5519
739062
739059
2026-04-22T00:49:10Z
AsteraBot
69554
blanking
739062
wikitext
text/x-wiki
phoiac9h4m842xq45sp7s6u21eteeq1
739063
739062
2026-04-22T00:54:01Z
AsteraBot
69554
Bot: archiving discussions
739063
wikitext
text/x-wiki
<div class="boilerplate metadata discussion-archived" style="background-color: #F2F4FC; margin: 2em 0 0 0; padding: 0 10px 0 10px; border: 1px solid #aaa">
:''The following discussion is closed. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.''
::With the unanimous support of 9 community members, this request is '''successful'''. Congratulations on being the first officially elected steward and welcome (back?) to the team! :) [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 21:10, 21 April 2026 (UTC)
----
Hello! I'm Justarandomamerican, and I am filing a request for stewardship, because the board has abolished the staff role, and for staff members to keep their permissions, they must stand for election. I've been here since 2024, when the farm was founded. I hope the community trusts me. Thank you for your time! [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 21:10, 16 April 2026 (UTC)
:<small> This candidate has signed an NDA with the WikiOasis Foundation, as is required for stewardship. [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 01:23, 17 April 2026 (UTC)</small>
=== Questions ===
In the interests of making this more of an interesting process than a formality, I have a few questions which more or less mirror to all candidates. Some I admit are bundled questions, feel free to answer to the extent applicable.
# Based on your request, what do you currently do that requires Steward capabilities?
# Do you believe you have made mistakes in your official capacity on this platform so far? If so, what did you do to improve from those mistakes? You could elaborate on just one if you like, or a couple.
# What is your vision of what a steward is, and what they do for the community?
# Two users appear in the CheckUser interface who both appear to be using Tor who are regular editors on a large wiki, which is also being attacked seemingly out of nowhere by an assailant who is using Tor and free VPNs. Is this a slam dunk connection, if your review of edits suggest their editing styles are similar (but no major stylistic tells)? Why/why not, and if not, what's the next step to find out?
# How would you close a vote proposing a change in which seven people essentially say support and maybe a few words, nothing really in depth, and three people have articulated major concerns with the proposal at the level of detail of these questions? Would that process change if it was only one oppose like this to seven support, and if that oppose appeared just before you were going to close the RfC?
Thank you, --'''[[User:Raidarr|raidarr]]''' '''('''[[User_talk:Raidarr|📡]]''')''' 22:45, 17 April 2026 (UTC)
:Hello! Sorry I was unable to respond; I have other things going on.
:# CheckUser and Oversight, definitely; I need those to help with abuse and help our wikis out.
:# Oh yes, definitely. I have made mistakes, including unauthorized usage of Foundation funds. I have been working on impulse control, and will be in therapy for the foreseeable future. Thank you for asking.
:# A steward is, fundamentally, a community representative. I represent the community, even though I might have some control over it, I shouldn't use it in an authoritarian manner.
:# This does not seem to be a slam dunk connection. I would keep watching their contribs to find out.
:# First one, would close as unsuccessful, it is not a consensus to have several support votes with no words, and a few major concerns articulated in detail. Second one, would relist. All opinions should have a chance to be considered, no matter how late they appeared.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:27, 20 April 2026 (UTC)
A few questions targeted towards individual candidates across the process:
# How do you see your history as president of the WikiOasis Foundation fits into this role, and the trust that the community will be placing in you that may not yet have been rebuilt?
# What do you think that you would bring to the steward team that other candidates may not?
# Can you discuss what your reasoning behind [https://italianbrainrot.wikioasis.org/wiki/Special:Log?logid=23008 this] was, as there were local administrators at the time who could've handled this apparent one off incident?
# How do you feel that team discussion fits into the handling of appeals by banned and locked users, do you think that an appeal should be solely handled by one person without discussion?
Best, --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 00:14, 18 April 2026 (UTC)
:# Honestly, I see it fitting into this role somewhat. It was a major breach of trust, and I have worked on myself since then. I just hope it never happens again, with anybody.
:# There are numerous experienced candidates, so nothing new there. I might just bring my track record of good behavior since the Foundation incident.
:# That was clear and obvious vandalism, which can be handled by anyone with rights to delete, including GPs and GAs.
:# I think there should be team discussion around appeals. One person handling it doesn't seem good, as they may be biased.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:32, 20 April 2026 (UTC)
=== Discussion ===
# {{support}}, has a clue, not a jerk --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 01:14, 17 April 2026 (UTC)
#{{Support}} [[User:SleepingElephant2145|SleepingElephant2145]] ([[User talk:SleepingElephant2145|talk]]) 01:20, 17 April 2026 (UTC)
# I have noticed Justarandomamerican around testwiki.wiki (where they serve as system admin/steward) and can {{support}} this request. <span style="font-family:Courier New;font-weight:bold;text-shadow:1px 1px 1px cyan">[[User:Tester|Tester]]</span> ([[User_talk:Tester|ᴛ]]•[[Special:Contributions/Tester|ᴄ]]) 01:40, 17 April 2026 (UTC)
# {{Support|strongest}} <span style="background:#EFD8FD;color:Indigo;font-family:serif">~ '''''[[User:Dream Indigo|<span style="color:Indigo">Dream Indigo</span>]]''''' [[User talk:Dream Indigo|<span style="color:Indigo">✩</span>]]</span> 01:49, 17 April 2026 (UTC)
# No brainer {{support}}. [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 01:53, 17 April 2026 (UTC)
# Ex-Board Member and is trusted within the WikiOasis Volunteers. No brainer {{support}}. [[User:Fearless|Fearless]] ([[User talk:Fearless|talk]]) 10:22, 17 April 2026 (UTC)
# {{Support}} --[[User:PinkPugPrincess|PinkPugPrincess]] ([[User talk:PinkPugPrincess|talk]]) 13:36, 17 April 2026 (UTC)
# {{Support|Strongest}} [[User:AlPaD|AlPaD]] ([[User talk:AlPaD|talk]]) 14:52, 17 April 2026 (UTC)
# {{Support}} No Concerns --[[User:Cocopuff2018|Cocopuff2018]] ([[User talk:Cocopuff2018|talk]]) 16:36, 17 April 2026 (UTC)
----
:''The above discussion is preserved as an archive. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.'' </div>
8lej643z0xe21spnp3hs40wko87u4qo
739066
739063
2026-04-22T00:56:58Z
AsteraBot
69554
Bot: archiving discussions
739066
wikitext
text/x-wiki
<div class="boilerplate metadata discussion-archived" style="background-color: #F2F4FC; margin: 2em 0 0 0; padding: 0 10px 0 10px; border: 1px solid #aaa">
:''The following discussion is closed. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.''
::With the unanimous support of 9 community members, this request is '''successful'''. Congratulations on being the first officially elected steward and welcome (back?) to the team! :) [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 21:10, 21 April 2026 (UTC)
----
Hello! I'm Justarandomamerican, and I am filing a request for stewardship, because the board has abolished the staff role, and for staff members to keep their permissions, they must stand for election. I've been here since 2024, when the farm was founded. I hope the community trusts me. Thank you for your time! [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 21:10, 16 April 2026 (UTC)
:<small> This candidate has signed an NDA with the WikiOasis Foundation, as is required for stewardship. [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 01:23, 17 April 2026 (UTC)</small>
=== Questions ===
In the interests of making this more of an interesting process than a formality, I have a few questions which more or less mirror to all candidates. Some I admit are bundled questions, feel free to answer to the extent applicable.
# Based on your request, what do you currently do that requires Steward capabilities?
# Do you believe you have made mistakes in your official capacity on this platform so far? If so, what did you do to improve from those mistakes? You could elaborate on just one if you like, or a couple.
# What is your vision of what a steward is, and what they do for the community?
# Two users appear in the CheckUser interface who both appear to be using Tor who are regular editors on a large wiki, which is also being attacked seemingly out of nowhere by an assailant who is using Tor and free VPNs. Is this a slam dunk connection, if your review of edits suggest their editing styles are similar (but no major stylistic tells)? Why/why not, and if not, what's the next step to find out?
# How would you close a vote proposing a change in which seven people essentially say support and maybe a few words, nothing really in depth, and three people have articulated major concerns with the proposal at the level of detail of these questions? Would that process change if it was only one oppose like this to seven support, and if that oppose appeared just before you were going to close the RfC?
Thank you, --'''[[User:Raidarr|raidarr]]''' '''('''[[User_talk:Raidarr|📡]]''')''' 22:45, 17 April 2026 (UTC)
:Hello! Sorry I was unable to respond; I have other things going on.
:# CheckUser and Oversight, definitely; I need those to help with abuse and help our wikis out.
:# Oh yes, definitely. I have made mistakes, including unauthorized usage of Foundation funds. I have been working on impulse control, and will be in therapy for the foreseeable future. Thank you for asking.
:# A steward is, fundamentally, a community representative. I represent the community, even though I might have some control over it, I shouldn't use it in an authoritarian manner.
:# This does not seem to be a slam dunk connection. I would keep watching their contribs to find out.
:# First one, would close as unsuccessful, it is not a consensus to have several support votes with no words, and a few major concerns articulated in detail. Second one, would relist. All opinions should have a chance to be considered, no matter how late they appeared.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:27, 20 April 2026 (UTC)
A few questions targeted towards individual candidates across the process:
# How do you see your history as president of the WikiOasis Foundation fits into this role, and the trust that the community will be placing in you that may not yet have been rebuilt?
# What do you think that you would bring to the steward team that other candidates may not?
# Can you discuss what your reasoning behind [https://italianbrainrot.wikioasis.org/wiki/Special:Log?logid=23008 this] was, as there were local administrators at the time who could've handled this apparent one off incident?
# How do you feel that team discussion fits into the handling of appeals by banned and locked users, do you think that an appeal should be solely handled by one person without discussion?
Best, --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 00:14, 18 April 2026 (UTC)
:# Honestly, I see it fitting into this role somewhat. It was a major breach of trust, and I have worked on myself since then. I just hope it never happens again, with anybody.
:# There are numerous experienced candidates, so nothing new there. I might just bring my track record of good behavior since the Foundation incident.
:# That was clear and obvious vandalism, which can be handled by anyone with rights to delete, including GPs and GAs.
:# I think there should be team discussion around appeals. One person handling it doesn't seem good, as they may be biased.
:[[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 15:32, 20 April 2026 (UTC)
=== Discussion ===
# {{support}}, has a clue, not a jerk --'''[[User:Zippy|zippy]]'''[[User talk:Zippy|bonzo]]''' ([[Special:Contributions/Zippy|c]] • [[Special:CentralAuth/Zippy|ca]]) 01:14, 17 April 2026 (UTC)
#{{Support}} [[User:SleepingElephant2145|SleepingElephant2145]] ([[User talk:SleepingElephant2145|talk]]) 01:20, 17 April 2026 (UTC)
# I have noticed Justarandomamerican around testwiki.wiki (where they serve as system admin/steward) and can {{support}} this request. <span style="font-family:Courier New;font-weight:bold;text-shadow:1px 1px 1px cyan">[[User:Tester|Tester]]</span> ([[User_talk:Tester|ᴛ]]•[[Special:Contributions/Tester|ᴄ]]) 01:40, 17 April 2026 (UTC)
# {{Support|strongest}} <span style="background:#EFD8FD;color:Indigo;font-family:serif">~ '''''[[User:Dream Indigo|<span style="color:Indigo">Dream Indigo</span>]]''''' [[User talk:Dream Indigo|<span style="color:Indigo">✩</span>]]</span> 01:49, 17 April 2026 (UTC)
# No brainer {{support}}. [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 01:53, 17 April 2026 (UTC)
# Ex-Board Member and is trusted within the WikiOasis Volunteers. No brainer {{support}}. [[User:Fearless|Fearless]] ([[User talk:Fearless|talk]]) 10:22, 17 April 2026 (UTC)
# {{Support}} --[[User:PinkPugPrincess|PinkPugPrincess]] ([[User talk:PinkPugPrincess|talk]]) 13:36, 17 April 2026 (UTC)
# {{Support|Strongest}} [[User:AlPaD|AlPaD]] ([[User talk:AlPaD|talk]]) 14:52, 17 April 2026 (UTC)
# {{Support}} No Concerns --[[User:Cocopuff2018|Cocopuff2018]] ([[User talk:Cocopuff2018|talk]]) 16:36, 17 April 2026 (UTC)
----
:''The above discussion is preserved as an archive. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.'' </div>
== Justarandomamerican (Steward)==
<div class="boilerplate metadata discussion-archived" style="background-color: #F2F4FC; margin: 2em 0 0 0; padding: 0 10px 0 10px; border: 1px solid #aaa">
:''The following discussion is closed. <b style="color:red">Please do not modify it</b>. Subsequent comments should be made in a new section.''
::With the unanimous support of 9 community members, this request is '''successful'''. Congratulations on being the first officially elected steward and welcome (back?) to the team! :) [[User:Globe|Globe]] ([[User talk:Globe|talk]]) 21:10, 21 April 2026 (UTC)
----
Hello! I'm Justarandomamerican, and I am filing a request for stewardship, because the board has abolished the staff role, and for staff members to keep their permissions, they must stand for election. I've been here since 2024, when the farm was founded. I hope the community trusts me. Thank you for your time! [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 21:10, 16 April 2026 (UTC)
:<small> This candidate has signed an NDA with the WikiOasis Foundation, as is required for stewardship. [[User:Justarandomamerican|Justarandomamerican]] ([[User talk:Justarandomamerican|talk]]) 01:23, 17 April 2026 (UTC)</small>
haajgmm8ovdrxft35dtroda4rco1ijb