Wikifunctions
wikifunctionswiki
https://www.wikifunctions.org/wiki/Wikifunctions:Main_Page
MediaWiki 1.47.0-wmf.5
first-letter
Media
Special
Talk
User
User talk
Wikifunctions
Wikifunctions talk
File
File talk
MediaWiki
MediaWiki talk
Template
Template talk
Help
Help talk
Category
Category talk
TimedText
TimedText talk
Module
Module talk
Translations
Translations talk
Event
Event talk
Wikifunctions:Project chat
4
1184
281657
280948
2026-06-08T03:08:06Z
SpBot
978
archive 2 sections: 1 to [[Wikifunctions:Project chat/Archive/2026/04]], 1 to [[Wikifunctions:Project chat/Archive/2026/05]] - previous edit: [[:User:SpBot|SpBot]], 2026-06-06 03:08
281657
wikitext
text/x-wiki
{{shortcut|[[WF:CHAT]]|[[WF:PC]]|[[WF:VP]]}}
__NEWSECTIONLINK__
[[Category:Help]] <!-- please do not remove this line -->
Welcome to the Project chat, a place to discuss any and all aspects of Wikifunctions: the project itself, policy and proposals, individual data items, technical issues, etc.
Other places to find help:
* [[Wikifunctions:Administrators' noticeboard]]
* [[Wikifunctions:Report a technical problem]]
* [[Wikifunctions:FAQ]]
{{Autoarchive resolved section
|age = 1
|archive = ((FULLPAGENAME))/Archive/((year))/((month:##))
|timeout=30
}}
{{Archives|{{#tag:div|<br />{{Flatlist|{{Special:PrefixIndex/WF:Project chat/Archive/|stripprefix=1|hideredirects=1}}
|class=mw-collapsible-content|style=font-size:92%;}}|class="mw-collapsible mw-collapsible-toggle mw-collapsed"}}
|prefix=WF:Project chat/Archive/
}}
== Implementation of rational number in JS doesn't match in Z19677 (Rational number) and Z28579 (RGBA colour) ==
In {{Z|19677}} it's
<syntaxhighlight lang=js>
{
"K1": sign * numerator,
"K2": denominator
}
</syntaxhighlight>
but in {{Z|28579}} it's
<syntaxhighlight lang=js>
[ sign * numerator, denominator ]
</syntaxhighlight> '''<span style="font-family:Iosevka,monospace">[[User:沈澄心|<span style="color:#9f3526">dring</span>]][[User talk:沈澄心|<span style="color:#534fa3">sim</span>]]</span>''' 05:15, 4 May 2026 (UTC)
:I'm guessing this is why [[Z34743]] fails all the tests. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 01:00, 18 May 2026 (UTC)
:Moved to [[Talk:Z28579#Mismatching_JS_code_representation|Talk:Z28579]] so this doesn't get lost, and made a request on [[Wikifunctions:Administrators'_noticeboard#RGBA_colour's_type_converters_don't_match_Rational|the Administrators' noticeboard]]. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 17:39, 5 June 2026 (UTC)
== RGBA colour, spelling... ==
Something that has always irked me a little bit is the spelling of [[Z28579|RGBA colour (Z28579)]]. I guess this is not unsurprising for me considering my use of US English but I think there is more to it than preference and I want to try to argue for it being changed to use American spelling. I know that this probably has a snowball's chance in hell of actually garnering any support, so I won't really be miffed if the spelling remains as it is, but I thought it wouldn't hurt to raise this regardless.
The main issue I have with it is the spelling of the original proposal. When infernostars raised the [[Wikifunctions:Type proposals/RGBA color|type proposal]], the spelling was 「RGBA color」. Of the comments that mentioned the word 「colo[u]r」, two used British spelling while six used the American spelling as used in the proposal. The only thing that really pointed to the use of ''colour'' was the fact that the catalog page on color functions used that spelling already. For all intents and purposes, the spelling of the original proposal should have been maintained, but it was not; [[User:DVrandecic (WMF)|DVrandecic]], the eventual creator of the type, used a different spelling.
It should be noted that there was really no reason for this to occur and while it is an undoubtedly minor issue I still believe it should be rolled back and the type should use the spelling of the original proposal and majority of editor comments. In [[abstract:Q936|OpenStreetMap]], there have been keyvalue proposals that have had the finalized spelling that gets put to use be in British English despite the original proposal being in American English; this has usually occurred with proposals relating to 「X center/centre」 tags. This makes sense on the surface, because OpenStreetMap is maintained by a UK organization, and still has close ties to Europe. The Wikimedia Foundation, however, is an ''American'' company. This is often brought up as a fallible argument when debating article spelling on the English Wikipedia, and I don't bring it up to support that 「RGBA color」 should be used for that exact reason, but rather to state that OpenStreetMap's general policy on tag names need not apply here. It appears to me that, at least initially, the majority of 「core contributors」 to Wikifunctions used British English; I can name YoshiRulz, 99of9, GrounderUK, and VIGNERON.<ref group="color">I'm avoiding linking to these folks because I don't think pinging them about this discussion is all too necessary unless they themselves want to be involved; I don't want to clutter their inboxes just to briefly mention them. I pinged Denny because, well, I'm asking him a question directly, but everyone else I would prefer to join this discussion by their own accord... not that I wish for this decision to be confused as me going 「these people use British English so they will probably oppose my idea, I won't invite them to the discussion because of that」...no, I promise you that is not the reason.</ref> I see (or saw) these people ''everywhere'', so it makes sense that British English has prevailed in some sorts on this website, but I don't think that indicates that it should be the ''preferred'' spelling across the website, at least not to the point where a proposal should have its name changed to match such a "consensus".<ref group="color">It could be argued that the front-and-center ''Function catalogue'' using 「catalogue」 is actually indicative of such a "consensus", but ''catalogue'' is in a similar position to the word ''grey'' where I live (that is, the US) in that it is used just as often as its American counterpart. Also, consider Wiktionary's ''Beer parlour'' project chat.</ref>
The unnecessary modification of the original spelling is my main argument for changing it back... but of course, I must obligatorily state that on English Wikipedia, it is [[w:Color|Color]] and [[w:RGBA color model|RGBA color model]]; on Wikidata, it is [[d:Q1075|color]] and [[d:Q2325624|RGBA color space]]; in CSS (which typically uses hexadecimal triplets to specify RGBA values), the properties are <code>color</code>, <code>background-color</code>, etc.; bit of a weak jab, but on Schema.org it is [https://schema.org/color color], [https://schema.org/colorSwatch colorSwatch]; et cetera. {{Z|Z28580}} uses ''color'', so does {{Z|Z28591}} and its Python counterpart.
Mr. Vrandečić, I have to ask, I'm rather confused... you created the color type using British English spelling, but you were also responsible for the creation of the equality function which uses the American English spelling. You also seem to be writing in American English for the status updates, judging by your use of -''ize'' over -''ise'' endings and use of ''program'' over ''programme'' in [[Wikifunctions:Status updates/2026-04-16]]. Is there something I'm missing or have you switched your preferred variant somewhere along the way?
Anyways, do consider this if you wish... again, I don't suppose this will garner much support, it is the ''non-issuest'' of ''non-issues'', but it has irked me to the point where I want to ask about it to get some answers, if nothing else. I am not arguing for every other color function to have its name changed, just the type itself.
<references group="color"></references>
— [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 14:04, 8 May 2026 (UTC)
:This is a multilingual project; the <code>en</code> label is <code>RGBA colour</code> and the <code>en-us</code> label is <code>RGBA color</code>. Though I'm not able to switch to <code>en-us</code> via the language picker so that would need to be fixed.<!--
--><br>edit after reading your whole comment: The same is true of {{Q|1075}}, there are labels specified for multiple English variants. (In {{Q|2325624}} it's only an alias.) I agree that other websites' choices aren't binding on us, but from that, I conclude that the more widespread British/Commonwealth spellings should be used for the generic <code>en</code>. As for myself, I'm Aussie and I will continue to use the BrE spellings ([[w:en:Oxford_spelling#Language_tag_comparison|+ "routing"]], TIL) if only by muscle memory.<!--
--><br>[[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 17:42, 8 May 2026 (UTC)
:: Your lattermost point would normally be fine in a perfect world. Wikipedia's <code>convert</code> function defaults to "international" English, which I don't personally take issue with because it happens that we here in America are actually outliers for saying and spelling things differently... err, or we were for a while at least, nowadays it seems like an even split (plus you have "yield" vs. "give way" which is effectively the logical opposite of US's use of "meter" over "metre").
:: However, this is not a perfect world, and I don't think <code>en</code> should correspond to any particular variant. It is too fragmented across all software at this point to impose such a requirement. The inability to switch to <code>en-us</code> on this website foregoes an easy and simple solution to this problem that makes everyone happy, because the yanks (such as myself) can't be happy because we can't see the labels in American English even if we wanted to, and the other folk can't switch either as far as I'm aware (and the en-CA and en-GB languages in the preferences page seems to be deprecated). My point being, <code>en</code> is abused to mean "en-UK" just as often as it is abused to mean <code>en-US</code>; I think a decision shouldn't be made on such an assumption of one "default". — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 14:48, 12 May 2026 (UTC)
:Hi @[[User:Theki|rae]]! I have no opinion nor preference on this, and given my background, I am just entirely confused about my spelling preferences myself, as you can tell from my inconsistent usage. I learned British English in school and used that for maybe two decades or so, but moved to the US and lived there for more than a decade, enough to be naturalized, but now I am back in Europe and I am technically a professor at King's College London, soooo.... honestly, I do not know. I don't remember having put too much thought into it at the moment I created it. The good thing is that in Wikifunctions, just as in Wikidata, it is easy to change, without messing things up too much (unlike in Wikipedia), so my suggestion is, just make the change, see if anyone complains, and if they do, discuss it more. I don't know if there is a guideline already in Wikifunctions about the variants. I am happy either way, and honestly, I keep forgetting which variant is which most of the time. --[[User:DVrandecic (WMF)|DVrandecic (WMF)]] ([[User talk:DVrandecic (WMF)|talk]]) 18:16, 10 May 2026 (UTC)
:: I can definitely understand this, although I am unfortunately rather passionate about any minutiae involving preferential minor differences in ''anything'', of which AmE vs. BrE chiefly is. So I dedicate a lot of headspace to it. More than I should. Not that I wish to imply that the comment above that I have wrote is of an irrational nature, or done out of spite or pure emotion and subjectivity; I do genuinely believe that ''RGBA color'' is beyond just a personal preference and is just logical. I may boldly go and change it, but for some reason I was expecting that changing the English label of a Type would require elevated permissions, and I also didn't want to do it only to get immediately reverted because it ''did'' strike a chord with someone, when I could instead see how apathetic, supportive, or in opposition interested people are beforehand and ''then'' act accordingly. I was not meaning to antagonize you over your spelling habits, I did actually use British English for a few years starting in 2020 before I went back to American English, so I'd be a hypocrite for me to decry you for not always sticking to some arbitrary standard of spelling words over the other. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 14:55, 12 May 2026 (UTC)
:Although I spell it “colour”, I think it makes more sense to use “color” for the type, since that is almost always the required spelling when the string functions as a keyword.
:More generally, though, Wikidata’s lexicographic data happens to favour “colour” over “color” and (quite rightly, in my view) lacks a specific representation for "en". This is unusual, in my experience, as "en" is widely misused in place of "en-US", where there are recorded spelling differences.
:(I would also say it is standard British English to use “program” in a programming context and “programme” elsewhere. Use of -ize rather than -ise is a matter of personal preference or house style, but regional autocorrect encourages -ise.) [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 11:00, 12 May 2026 (UTC)
:: ''Wikidata’s lexicographic data happens to favour “colour” over “color” and (quite rightly, in my view) lacks a specific representation for "en"''
:: Definitely agreeing with you on the latter being a good choice. However, I suspect the favoring of "colour" over "color" may be because, in terms of language codes, when sorted alphabetically <code>en-us</code> actually comes ''after'' <code>en-gb</code>. Although, the frontend seems to be sorting <code>en-ca</code> after <code>en-gb</code>, so I don't actually know how correct that is.
:: ''I would also say it is standard British English to use “program” in a programming context and “programme” elsewhere''
:: The context of the spelling was "''No program for the NLG SIG meeting for next Tuesday has been proposed''". In that usage context, I think it makes sense to assume that ''program'' is not being used to refer to a computer program, but to a ''program of events'' or similar, something that you would spell as a ''programme'' in British English. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 15:02, 12 May 2026 (UTC)
:{{s}} this. I'm obviously biased but I believe American English is preferable generally, American dominance on the internet (our Department of Defen'''s'''e invented it!) and rapidly-increasing consumption of American media by international English speakers means that more people use American English's conventions, this is clear through for example [http://trends.google.com/explore?q=color%2Ccolour&date=all&geo=Worldwide search trends] (though they aren't particularly reliable). Perhaps this is a bit of a supremacist opinion, but we should have internal consistency, and if we must choose, American English should be our first choice (then Indian and then British English) [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 14:10, 12 May 2026 (UTC)
:: This is rather flawed reasoning, though. I think probably any given British or Indian person would not agree on using that as the reasoning for this, not that you are necessarily ''completely wrong'', but if this is not a good enough reason for English Wikipedia's (admittedly extremely flawed) ''ENGVAR'' policy then I don't think it's likely it will pass here either.
:: Although of note is that [https://books.google.com/ngrams/graph?content=color%2Ccolour&year_start=1800&year_end=2022&corpus=en&smoothing=3&case_insensitive=true Google ngrams] agree with you, but "color" vs. "colour" is an eternal holy war that will not be won by demonstrating that more books use US spelling over Commonwealth spelling. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 14:44, 12 May 2026 (UTC)
:::You're probably right that it's not very sound. I'm biased in that other varieties of English irk me, and that's probably mutual for people who are used to other varieties of English when they read what I write! [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 14:56, 12 May 2026 (UTC)
:I've decided to boldly [[Special:Permalink/274271|make the change]]. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 15:02, 12 May 2026 (UTC)
:: Thank you. Considering both you and GrounderUK seem to consider it an okay change, I think this will do for now.
:: I should note that the matter of whether to move [[Wikifunctions:Catalogue/Colour functions]] in response to this (however this discussion will ultimately turn out) is a whole other can of worms, in my view. I can't say I have an opinion on that at the moment, but I'm putting it out there regardless. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 15:06, 12 May 2026 (UTC)
:::Personally, I'm in favor of moving the page and renaming all of the items on it. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 15:10, 12 May 2026 (UTC)
::I don't like this (exactly because of the American hegemony you cited), but again, it shouldn't matter because the software is meant to be multilingual. Clearly there's a bug preventing you from picking an English variant/dialect as your display language. But the search bar and Function/Type autocompletion do check the English variants for matches. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 15:15, 12 May 2026 (UTC)
== Proposals on the architecture of Abstract Content rendering ==
Starting from a discussion born on the Telegram chat, I've explained two different proposals on how the NLG on Abstract Wikipedia should be organized in the page [[abstract:User:Dv103/Abstract articles architectures]]. Please come to contribute to the discussion, or to propose alternatives. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 14:31, 11 May 2026 (UTC)
:Thank you for dedicating your time to writing this, it is very informative. I will try to add input once I'm not in over my head with finals. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 16:27, 12 May 2026 (UTC)
== Display function for HTML fragment ==
Currently, any collapsed Z89 literal appears as<blockquote><> [[Z89|HTML fragment]]</blockquote>If I were to create a new Function which returned something like<blockquote><> 123-byte HTML fragment <q><nowiki><td><span lang=</nowiki>…</q></blockquote>could that be connected to replace the collapsed form, or would it require changes to the Wikilambda software? [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 16:14, 11 May 2026 (UTC)
:It might work, but I doubt it. Those angled brackets suggest that the collapsed form is not simply defaulting to the type’s label. Looking at [[:phab:T410509]], I’ve concluded that enhancements to the collapsed form were never considered, rather than being actively rejected. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 12:12, 12 May 2026 (UTC)
::[[:Phab:T391985]] documents the original design. Note the fifth bullet point under “Acceptance criteria”. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 12:21, 12 May 2026 (UTC)
:I'm not sure the byte-size is necessary, but the outer tag (or first outer tag, though generally I'd prefer most fragments use a wrapper tag if it needs multiple like JSX does, but that's a whole different topic) would be nice. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 12:51, 12 May 2026 (UTC)
== Wikifunctions & Abstract Wikipedia Newsletter #248 is out: A higher meaning ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-05-15|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we discuss functions creating language fragments, we present our latest news in Types, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 14:36, 15 May 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30536976 -->
== [[Z34510]] ==
This function, which determines if a Wikidata item for a {{q|5}} has an undeprecated {{p|21}} statement of {{q|6581097}}, returns false for {{q|173399}}, a transgender man. This is because his item assigns his P21 statement to {{q|2449503}}, not {{q|6581097}}. I'm not sure how to account for this discrepancy. Should {{z|34510}}:
# Include {{q|2449503}} as a value that can lead to a true result,
# Not include {{q|2449503}} as a value that can lead to a true result, while another function (e.g., "Q5 is a man?") could return true for either "male" or "trans man",
# Not include {{q|2449503}} as a value that can lead to a true result, while another function (e.g., "Q5 is a trans man?") could return true for "trans man",
# Not exist at all?
[[User:JJPMaster|JJP]]<sub>[[User talk:JJPMaster|Mas]]<sub>[[Special:Contributions/JJPMaster|ter]]</sub></sub> ([[wikt:she|she]]/[[wikt:they|they]]) 16:48, 16 May 2026 (UTC)
:I can't think of a single use case where you would need to determine if a person is a cisgender man and nothing else. Functions are good for generalizing across multiple possibilities when they exist, so I think it would be best if trans men were considered a part of the criteria for returning a true value. If asking for specifically {{q|6581097}}s and ''nothing'' else was desired then the function name would be a misnomer as Elliot Page is inarguably a male (at least in the view of most reasonable and intelligent people). — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 19:03, 16 May 2026 (UTC)
:You made the function in the first place; what were you planning on using it for? AW? Maybe it should return a {{Z|25501}} which can then be passed on to other NLG functions. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 20:01, 16 May 2026 (UTC)
== Lexeme from wikidata label, or "best" lexeme from wikidata item ==
I was looking into fixing [[Z28028]]. I found that I could add "requires grammatical feature: definite article" to "United Kingdom" (L8558). Now I'm stuck on how to get to that lexeme from {{Q|145}}. There's [[Z23471]], but that for very good reason gives you multiple lexemes with the same sense, and I just want the best one like how the label is always the best string. Is there a function that can do this?
There's definitely the case of a Wikidata label that isn't a lexeme (most commonly multiple lexemes) but I'm only considering the case where it is one lexeme here. [[User:Aaron Liu|Aaron Liu]] ([[User talk:Aaron Liu|talk]]) 20:02, 16 May 2026 (UTC)
:There is {{Z|Z27327}}, that tries to give the best lexeme through various heuristics. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 22:22, 16 May 2026 (UTC)
:: Wonderful! I did stumble upon [[Z33818]] but this is perfect. [[User:Aaron Liu|Aaron Liu]] ([[User talk:Aaron Liu|talk]]) 00:25, 17 May 2026 (UTC)
== [[Z29591]] isn't working for me ==
For instance, trying to manually put in the exact inputs for one of the test cases just returns an empty Monolingual text. See [https://www.wikifunctions.org/wiki/Z29591?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z29591%22%2C%22Z29591K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q3257809%22%7D%2C%22Z29591K2%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q21264361%22%7D%2C%22Z29591K3%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q22006653%22%7D%2C%22Z29591K4%22%3A%22Z1002%22%7D]. [[User:JJPMaster|JJP]]<sub>[[User talk:JJPMaster|Mas]]<sub>[[Special:Contributions/JJPMaster|ter]]</sub></sub> ([[wikt:she|she]]/[[wikt:they|they]]) 01:17, 17 May 2026 (UTC)
:You used [[:d:Q22006653]] rather than [[:d:Q1075]]. It looks like the [https://www.wikifunctions.org/wiki/Special:RunFunction?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30784%22%2C%22Z30784K1%22%3A%7B%22Z1K1%22%3A%22Z11%22%2C%22Z11K1%22%3A%22Z1002%22%2C%22Z11K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z21394%22%2C%22Z21394K1%22%3A%5B%22Z6%22%2C%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22664%22%2C%22Z22664K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q22006653%22%7D%2C%22Z22664K2%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q21264361%22%7D%2C%22Z22664K3%22%3A%22Z1002%22%7D%5D%7D%7D%7D explanatory error] is suppressed by the [https://www.wikifunctions.org/view/en/Z30009?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30009%22%2C%22Z30009K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30784%22%2C%22Z30784K1%22%3A%7B%22Z1K1%22%3A%22Z11%22%2C%22Z11K1%22%3A%22Z1002%22%2C%22Z11K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z21394%22%2C%22Z21394K1%22%3A%5B%22Z6%22%2C%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22664%22%2C%22Z22664K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q22006653%22%7D%2C%22Z22664K2%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q21264361%22%7D%2C%22Z22664K3%22%3A%22Z1002%22%7D%5D%7D%7D%7D%2C%22Z30009K2%22%3A%22Z801%22%7D final transformation]. The returned result is not actually empty; if you expand it, you can see that it is an unresolved function call. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 09:59, 17 May 2026 (UTC)
== [[Z35298]] ==
Does anyone know what the problem with this implementation is? [[User:JJPMaster|JJP]]<sub>[[User talk:JJPMaster|Mas]]<sub>[[Special:Contributions/JJPMaster|ter]]</sub></sub> ([[wikt:she|she]]/[[wikt:they|they]]) 21:14, 18 May 2026 (UTC)
:There is a bug that doesn't allow Python implementation to return nested lists. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 05:31, 19 May 2026 (UTC)
::Is there a Phabricator task for this? Searching through them is hell. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 03:22, 20 May 2026 (UTC)
:::A bit of time ago I opened [[phab:T392750]], which is very similar to this issue. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 05:26, 20 May 2026 (UTC)
== May 2026 Wikimedia Café meetups regarding the Wikimedia Foundation Annual Plan ==
<div class="border-box" style="background-color: var(--background-color-warning-subtle, #f8eaba); max-width: 875px; padding: 5px; border: 1px solid black; margin: 5px; color: var(--clr-dark)">
<div class="box" style="float:left; padding-top: 15px; padding-right: 15px;">[[File:Wikimedia Café logo in plain SVG format.svg|75px|alt=The logo for the Wikimedia Café]]</div>
Hello! There will be two '''[https://meta.wikimedia.org/wiki/Wikimedia_Caf%C3%A9 Wikimedia Café]''' discussion opportunities during the last weekend of May. Both sessions will focus on the [https://meta.wikimedia.org/wiki/Wikimedia_Foundation_Annual_Plan/2026-2027 the 2026-2027 Wikimedia Foundation Annual Plan]. Participants may attend either or both sessions.
#'''Saturday, 30 May 2026 at 15:00 UTC''' ([https://zonestamp.toolforge.org/1780153200 timestamp converter]), at a time friendly to the Americas, Africa, and Europe
#'''Sunday, 31 May 2026 at 05:00 UTC''' ([https://zonestamp.toolforge.org/1780203600 timestamp converter]), at a time friendly to Asia and the Pacific
Café participants are highly encouraged to read in advance [https://en.wikipedia.org/wiki/User:Sohom_Datta/annual_plan_guide at least this summary of the plan]. Optionally, Café participants are encouraged to read portions of the plan that interest them and [https://meta.wikimedia.org/wiki/Talk:Wikimedia_Foundation_Annual_Plan/2026-2027 ask questions or provide feedback on the Annual Plan talk page].
Please see the Café page for more information, including [https://meta.wikimedia.org/wiki/Wikimedia_Caf%C3%A9#May_2026_meetings_with_a_focus_on_Wikimedia_Foundation_Annual_Plan/2026-2027 tables of timestamp conversions for both sessions], [https://meta.wikimedia.org/wiki/Wikimedia_Caf%C3%A9#Agenda._This_will_be_an_approximately_1_hour_Caf%C3%A9_session,_and_is_extendible_for_an_additional_30_minutes_if_needed. the agenda], and [https://meta.wikimedia.org/wiki/Wikimedia_Caf%C3%A9#How_to_attend_the_session how to register]!
<br />
[[File:Buntstifte Eberhard Faber crop 64h.jpg|860px|alt=cropped image of colored pencils]]</div>
<span style="white-space:nowrap;">[[User:Pine|<span style="color:#01796f; text-shadow:#00BFFF 0 0 1.0em">↠Pine</span>]] [[User talk:Pine|<span style="color:DeepSkyBlue">(<b style="color:#FFDF00;text-shadow:#FFDF00 0 0 1.0em">✉</b>)</span>]]</span> 19:56, 21 May 2026 (UTC)
== How to handle items without lexemes ==
NLG functions relay heavily on the presence of lexemes associated to items on Wikidata. But we know that not all the Wikidata items have an associated lexeme. There are multiple reasons why an item does not have an associated lexeme, like:
# The lexeme has not been created yet
# The item represents a place
# The item represents a person
# The item represents a specific concept that can only be expressed by a specific combination of words that cannot be [[Wikidata:Wikidata:Lexicographical data/Notability|notable]] (like {{Q|Q61220733}}).
My doubt is: what should we do with this fourth category? For many languages, just using the Wikidata item label is not possible, since it is necessary to conjugate the words or to retrieve grammatical information like the gender. What should we do? [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 16:09, 23 May 2026 (UTC)
:In that particular example, I think the thing to do is read its {{P|279}}: {{Q|21191270}}, then have some kind of heuristic based on that which says to take its {{P|8345}} and attach that Item's label to a Form of the word for "episode". In general, synthesising Lexemes for proper nouns is one of the problems that [[abstract:User:Dv103/Abstract_articles_architectures|proposals in your list here]] will have to address. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 22:59, 23 May 2026 (UTC)
[[File:Wikidata content 2024.svg|thumb|Content of Wikidata by type]]
::{{ping|Dv103}} very good point.
::For your point 2, it depends of the place but I think that quite often a lexeme can be created (most "Administrative territorial entity", most geographical entity, etc.). And with 3, your can add a lot of types (see pie chart) : Scholarly article, Human (with a very few exception), Wikimedia Category, Disambig, etc. which is (rough estimation) 2/3 of Wikidata items.
::A common rule (in dictionaries since forever and in Lexemes) is to not create an entry which is the "sum of its part". In this case, "Star Trek episode" is just episode + Star Trek, nothing more than its part. So logically, as {{U|YoshiRulz}} said, when no corresponding lexeme is found, the item should be decomposed the same way, the hard part is to know how to decompose it as the property will vary ; P31 and P279 are an obvious start but beyond that, I'm not sure we could find a general solution.
::PS: it's beyond you question but there is also the reverse problem, how to select one lexeme when multiple are linked to the same item...
::Cheers, [[User:VIGNERON|VIGNERON]] ([[User talk:VIGNERON|talk]]) 10:43, 24 May 2026 (UTC)
:::For the point 2, I think humans will be used way more than scholarly articles and disambiguations in NLG functions (outside references), that's why humans concern me more (still a cool pie chart, though).
:::For the reverse problem, there is already {{Z|Z27327}}: it's far from perfect, but usually makes a decent choice. Obviously it is not "complete", and probably it will never be complete, but it will have to be progressively improved by the community. And probably in the future we will need to create similar functions to select the best lexeme in more specific cases.
:::For my fourth point, I didn't think about the decomposition, but it is something that could be done with another never-complete community-mantained function, that progressively keeps being improved. If [[Wikifunctions:Type proposals/Semantic unit|semantic units]] will be implemented, through them it could actually be possible to do this operation in a laguage-independent way. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 12:10, 24 May 2026 (UTC)
::{{re|YoshiRulz}} Proper noun synthesis, along with other fallbacks for realizing the names of concepts that don't have lexemes, is merely a step within the overall abstract content rendering process and is not inherently tied to the process itself; having the ability to [https://gitlab.com/mahir256/ninai/-/blob/main/ninai/graph/client.py#L327 run any number of fallback mechanisms], instead of a raw call to (the equivalent of) Z27327, should be possible with any of the methods listed on the architectures page. [[User:Mahir256|Mahir256]] ([[User talk:Mahir256|talk]]) 16:41, 24 May 2026 (UTC)
== Wikifunctions & Abstract Wikipedia Newsletter #249 is out: Annual plan 2026-2027 ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-05-23|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we present you the current draft of objectives for Wikifunctions and Abstract Wikipedia in the WMF Annual Plan 2026-2027, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 09:48, 25 May 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30536976 -->
:The new <code>return_type</code> param to [[Special:ListObjectsByType]] will show Functions returning e.g. {{Z|27951}} and {{Z|882}} if those are typed in manually, but the dropdown menu doesn't offer them, probably because it's a copy of the dropdown above (and there are no Persistent objects of those Types). [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 10:12, 26 May 2026 (UTC)
::@[[User:YoshiRulz|YoshiRulz]]: Correct, it's filtering for Types, which includes "real" enums like [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&return_type=Z20342&orderby=name Day of Roman year (Z20342)]; light-weight enums have downsides as well as upsides, of which this is one. :-( [[User:Jdforrester (WMF)|Jdforrester (WMF)]] ([[User talk:Jdforrester (WMF)|talk]]) 14:39, 1 June 2026 (UTC)
:::I assume you mean {{Z|17402}}, since {{Z|20342}} is not an enumeration type? But I never mentioned enums: My hypothesis is that a Type appears in the dropdown iff there is a Persistent object of that type (<code>Z2K2.Z1K1</code>). Whereas I would expect a Type to appear in the dropdown iff there are any Functions which return objects of that type (<code>Z2K2.Z8K2</code>). Or just show every Type in the return type dropdown, since [[Special:ListObjectsByType/Z39|you already have a "no results" message]]. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 16:53, 1 June 2026 (UTC)
::::@[[User:YoshiRulz|YoshiRulz]]: Yes, you are correct, the concept of a Type here means "there is a Persistent object of that type". Other things (in practice, light-weight enums like Z27951) aren't Types. [[User:Jdforrester (WMF)|Jdforrester (WMF)]] ([[User talk:Jdforrester (WMF)|talk]]) 16:57, 1 June 2026 (UTC)
== Type documentation template ==
Over the past couple of weeks, I've been developing and rolling out {{t|type documentation}}: a standardised layout for Type metadata, de/constructors, conversions, etc. on each Type's talk page. (The layout is loosely based on [[d:Template:Property documentation|Wikidata's]].) See [[Talk:Z16683|Integer]] for an example that uses most of its features, and [[Talk:Z99|Quote]] for one that doesn't.<br>At this point I can't think of anything more to add besides [[Help:Comparison_function_table/float64|filling out]] a couple more [[Help:Type_conversion_table/Codepoint|tables]]. But if any of you have ideas or feedback, please click through to the relevant talk page and leave me a message. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 12:29, 26 May 2026 (UTC)
:I really like what you're doing here. Thank you. --[[User:99of9|99of9]] ([[User talk:99of9|talk]]) 13:26, 26 May 2026 (UTC)
:Yeah, nice work! I don’t think “Function declarations” is the best header for the collapsed table of searches by function signature, however. Now that it’s finally landed, we should probably include https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&return_type=Z16683 as well (outside the table). [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 16:23, 26 May 2026 (UTC)
== Apparent error in implementations of {{Z|20616}} ==
Please can I request help in how to understand a bug? Sorry if this is not the best place to ask.
I created lexemes [[d:Lexeme:L1566135|langue morte L1566135]] in French and [[d:Lexeme:L1566139|lengua muerta L1566139]] in Spanish, with property {{P|5185}} set to {{Q|Q1775415}} in each case. {{Z|20616}} should return a list of the grammatical genders of a given lexeme. It has two implementations, {{Z|Z20641}} and {{Z|Z21127}}, each of which works perfectly in the French case, returning a list containing Q1775415. But in the Spanish case, each of the two implementations wrongly returns an empty list. I cannot understand what is going wrong. How can I find out what is happening here? I would be grateful for any help or advice. [[User:Strobilomyces|Strobilomyces]] ([[User talk:Strobilomyces|talk]]) 13:56, 26 May 2026 (UTC)
:Both implementations return [https://www.wikifunctions.org/wiki/Z20616?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z20616%22%2C%22Z20616K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6825%22%2C%22Z6825K1%22%3A%7B%22Z1K1%22%3A%22Z6095%22%2C%22Z6095K1%22%3A%22L1566139%22%7D%7D%7D the same result]. As you added the gender only yesterday, I suppose it must have still been looking at a cached version of the lexeme from before that edit. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 16:06, 26 May 2026 (UTC)
::Thank you for answering. Yes, it works now. I thought it might have been something like that, but I waited more than 12 hours before testing it again today. I think that whenever SPARQL is in use, there will be caching issues, and it is a very bad problem. Is there any way of clearing the cache, or knowing when the cache will next be cleared, or how long it is necessary to wait before the changes come through? [[User:Strobilomyces|Strobilomyces]] ([[User talk:Strobilomyces|talk]]) 18:59, 26 May 2026 (UTC)
:::Well, it depends on the cache. “Wikidata entities in the orchestrator cache timeout after 24 hours” [https://t.me/Wikifunctions/28369 according to] @[[User:DMartin (WMF)|DMartin (WMF)]]. There is currently no way to clear that. I don’t think we have a handy guide to the different caches in operation, but the “general” function-call cache should be reset for a particular function when that function is edited. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 22:05, 26 May 2026 (UTC)
::::I think this is very unfortunate for anyone doing tests in Wikifunctions. So there is a 24-hour delay even applying to changes in Wikidata due to the Wikifunctions orchestrator cache, apart from any other caches such as the SPARQL one. I notice that an intermediate-level call using the [[d:Lexeme:L1566139|lengua muerta L1566139]] lexeme change, {{Z|33725}}, now works on "latín es una lengua muerta.", but the top-level call {{Z|26039}} still does not find the correct gender. If I test the function every 12 hours, does that mean that the erroneous result will be produced for ever, because it will always take the bad value less than 24 hours old from the cache? [[User:Strobilomyces|Strobilomyces]] ([[User talk:Strobilomyces|talk]]) 13:45, 27 May 2026 (UTC)
:::::I can only sympathize.
:::::It seems to me that [https://www.wikifunctions.org/wiki/Z26039?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z26039%22%2C%22Z26039K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q397%22%7D%2C%22Z26039K2%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q45762%22%7D%2C%22Z26039K3%22%3A%22Z1003%22%7D this has been correct] for a couple of days. But in the general case, no, repeated use of cached results does not re-start the clock. That would indeed be most unfortunate! [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 13:59, 27 May 2026 (UTC)
::::::It still doesn't work for me, it says "latín es un lengua muerta." But the test on the top-level implementation page, {{Z|33725}}, does work now. By the way, really it should say "el latín es una lengua muerta.", but that is another issue. Anyway, thanks a lot for your help. [[User:Strobilomyces|Strobilomyces]] ([[User talk:Strobilomyces|talk]]) 14:06, 27 May 2026 (UTC)
:::::::Ah, yes… my mistake, sorry.
:::::::It should be [https://www.wikifunctions.org/view/en/Z26039?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z26039%22%2C%22Z26039K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q397%22%7D%2C%22Z26039K2%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q45762%22%7D%2C%22Z26039K3%22%3A%22Z1003%22%7D consistent now]. The “couple of days” is the clue here; we were getting a result from the function-call cache and this has now been refreshed by my edit. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 14:25, 27 May 2026 (UTC)
::::::::Yes, it all works now. Thanks. [[User:Strobilomyces|Strobilomyces]] ([[User talk:Strobilomyces|talk]]) 14:54, 27 May 2026 (UTC)
== Vote now in the 2026 U4C election ==
<section begin="announcement-content" />
Eligible voters are asked to participate in the 2026 [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee|Universal Code of Conduct Coordinating Committee]] election. More information–including an eligibility check, voting process information, candidate information, and a link to the vote–are available on Meta at the [[m:Special:MyLanguage/Universal_Code_of_Conduct/Coordinating_Committee/Election/2026|2026 Election information page]]. The vote closes on 2 June 2026 at [https://zonestamp.toolforge.org/1780358400 00:00 UTC].
Please vote if your account is eligible. Results will be available by 14 June 2026. -- In cooperation with the U4C,<section end="announcement-content" />
[[m:User:Keegan (WMF)|Keegan (WMF)]] ([[m:User talk:Keegan (WMF)|talk]]) 17:14, 27 May 2026 (UTC)
<!-- Message sent by User:Keegan (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30513860 -->
== [[Z35880]] ==
The code of this implementation is adapted directly from [https://github.com/sutton-signwriting/core/blob/master/src/swu/swu-re.js]. I'm not sure why this function only works for "null" and "sort". Every other input causes the function to return [[Z577]]. Does anyone know what could be going on here? [[User:JJPMaster|JJP]]<sub>[[User talk:JJPMaster|Mas]]<sub>[[Special:Contributions/JJPMaster|ter]]</sub></sub> ([[wikt:she|she]]/[[wikt:they|they]]) 21:47, 30 May 2026 (UTC)
:Could you creade testcases showing this? [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 21:55, 30 May 2026 (UTC)
::I determined that this problem was due to a problem with UTF encoding, and it has since been resolved. [[User:JJPMaster|JJP]]<sub>[[User talk:JJPMaster|Mas]]<sub>[[Special:Contributions/JJPMaster|ter]]</sub></sub> ([[wikt:she|she]]/[[wikt:they|they]]) 18:11, 31 May 2026 (UTC)
== Continued WASI runner problems ==
I've continued to experience {{z|576}} on {{z|35904}}, despite [[phab:T419933|the purported fix]]. See {{z|35945}}. [[User:JJPMaster|JJP]]<sub>[[User talk:JJPMaster|Mas]]<sub>[[Special:Contributions/JJPMaster|ter]]</sub></sub> ([[wikt:she|she]]/[[wikt:they|they]]) 18:29, 31 May 2026 (UTC)
:A possibly related issue while trying to add more rows in these articles :
:1. https://abstract.wikipedia.org/view/en/Q16038495
:2. https://abstract.wikipedia.org/view/en/Q13581178
:So, I stopped at 2 rows. [[User:Jsamwrites|John Samuel]] 20:40, 31 May 2026 (UTC)
== Wikifunctions & Abstract Wikipedia Newsletter #250 is out: Looking back and forward ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-05-30|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we present you a recollection of our work so far, now that we celebrate our 250th newsletter, we share with you a summary of our latest outreach activities, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 10:04, 1 June 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30606821 -->
== Wikifunctions & Abstract Wikipedia Newsletter #251 is out: The illustrated encyclopaedia ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-06-05|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we introduce our first function to import images on Abstract Wikipedia, we present our Functions of the Week, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Also, we remind you that if you have questions or ideas to discuss, the next '''Volunteers' Corner''' will be held on '''[https://zonestamp.toolforge.org/1780939800 June 8, at 17:30 UTC]''' ([https://meet.google.com/xuy-njxh-rkw link to the meeting]).
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 14:14, 5 June 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30606821 -->
== Questions on a simple fragment example "The Eiffel Tower is a monument" ==
Hello. I would like to be able to use the function {{Z|Z26039}} to generate sentences like "the Eiffel Tower is a monument" or "la torre Eiffel es un monumento" in Spanish. It already raises a lot of questions.
Question 1: I should be able to set the first input "entity" to {{Q|Q243}} and the second input "class" to {{Q|Q4989906}} and get the correct sentence, shouldn't I? Just checking.
Question 2: {{Z|Z26039}} calls a language-specific function like "Spanish article-less instantiating sentence" {{Z|Z26337}}, which uses the label of the Wikidata item to get the text for "Eiffel Tower", which is similar to the lemma of the lexeme. But this would not be acceptable in production, would it? The item label "belongs" to all Wikidata users, not to Abstract Wikipedia users, and there is no guarantee what it might contain, such as a parenthesis for disambiguation. Or am I wrong?
Question 3a: We need to have a lexeme for the combination "Eiffel Tower" in each language, don't we? For instance in languages with gender, the lexeme is the only place to find the gender. It is true that if we know that the equivalent of "Tower" is the head word, syntactical information can be found under the lexeme for "tower", and it would be good to use a system like that. But the only place that the syntactic dependency information could be located is under the lexeme.
Question 3b: At present for {{Z|Z26039}} etc. to work, we have to add any forms or syntax information to the lexeme of the whole phrase, such as "Eiffel Tower". But property {{Q|P5238}} with attributes {{Q|P9763}} and {{Q|P9764}} can be used to define the structure and avoid duplicating the syntax information. What lexeme would be used for "Eiffel" in this case? Would it be the same as a lexeme for {{Q|Q20882}}? That makes no sense to me. I propose that there should be a dummy lexeme in each language which could be added to {{Q|P5238}} instead of a real lexeme to mean "invariant element".
Question 4: As has already been pointed out elsewhere, the fragment functions do not work well with the initial definite article in languages like English, Spanish and German. Examples:
* "'''The Eiffel Tower is a monument.'''" The item label "Eiffel Tower" omits the article and so the result omits the initial "The" in English. French, Spanish and German are similar.
* "'''The Sun is a star.'''" Similarly the article is wrongly omitted, also in French, Spanish and German.
* "'''Westminster Abbey is a monument.'''" This is OK in English and German as no article is needed, but not in French or Spanish where it is, for instance "La Abadía de Westminster es un monumento".
* "'''Latin is a dead language.'''" Also this is OK in English and German but not in French or Spanish, where an article is needed.
* "'''Jupiter is a planet.'''". This does not need an article and is OK in all the languages; I include this to show that you cannot assume that there is an article in all cases in French and Spanish.
How should the language functions find out whether an article is needed? In some cases, where the lemma is a phrase like "Abadía de Westminster" in Spanish, I think that it could be deduced, but in general there is no rule to give the answer. Using different rendering functions according to the case is '''not''' a solution, although it might work for a few specific languages like these four. It would not be acceptable because there will be many, many other cases of syntactical choices to be made for all the different languages, and we cannot expect the person writing the abstract code to take them all into account. So I suppose that a declaration in the lexeme is needed to solve this problem. I suppose that there must already be linguistic terminology for this problem, but I don't know it.
I would be grateful for any comments on any of these questions. [[User:Strobilomyces|Strobilomyces]] ([[User talk:Strobilomyces|talk]]) 15:02, 5 June 2026 (UTC)
cp4zkgnk1y74nonoxepvjyzpmh7ztvv
Z10139
0
3748
281660
281193
2026-06-08T06:19:24Z
Dv103
11127
Added Z36169, Z36170, Z36172 e Z36173 to the approved list of test cases
281660
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z10139"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z6",
"Z17K2": "Z10139K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Eingabe"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1014",
"Z11K2": "Ntinye"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Input"
}
]
}
}
],
"Z8K2": "Z6",
"Z8K3": [
"Z20",
"Z36169",
"Z36170",
"Z36172",
"Z36173"
],
"Z8K4": [
"Z14"
],
"Z8K5": "Z10139"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "RIPEMD-160"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "RIPEMD-160"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1014",
"Z11K2": "RIPEMD-160"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Compute the RIPEMD-160 hash of the input and return it as output."
}
]
}
}
gd6abgqn4i0x29oo7zo6n5f50okd3p7
281661
281660
2026-06-08T06:19:29Z
Dv103
11127
Added Z36171 to the approved list of implementations
281661
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z10139"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z6",
"Z17K2": "Z10139K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Eingabe"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1014",
"Z11K2": "Ntinye"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Input"
}
]
}
}
],
"Z8K2": "Z6",
"Z8K3": [
"Z20",
"Z36169",
"Z36170",
"Z36172",
"Z36173"
],
"Z8K4": [
"Z14",
"Z36171"
],
"Z8K5": "Z10139"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "RIPEMD-160"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "RIPEMD-160"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1014",
"Z11K2": "RIPEMD-160"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Compute the RIPEMD-160 hash of the input and return it as output."
}
]
}
}
fywpuadgb15zq10iqujzyx5emn7saah
Wikifunctions:Administrators' noticeboard
4
9491
281444
281127
2026-06-07T17:55:41Z
Ameisenigel
44
/* Import request */ Reply
281444
wikitext
text/x-wiki
{{dynamite|title=Administrators' noticeboard|t=yes}}
{{Autoarchive resolved section
|age = 1
|archive = ((FULLPAGENAME))/Archive/((year))/((month:##))
|timeout=30
}}
{{Archives|{{Flatlist|{{Special:PrefixIndex/Wikifunctions:Administrators' noticeboard/Archive/|stripprefix=1}} }}}}
<!-- Add new reports below this line -->
== Inactive functioneers ==
* [[User:Autom]]
* <del>[[User:Butko]]</del>
* [[User:Egezort]]
* [[User:Elwinlhq]]
* <del>[[User:Habst]]</del>
* <del>[[User:Lucas Werkmeister]]</del>
* <del>[[User:Mahir256]]</del>
* <del>[[User:Papuass]]</del>
* [[User:Renamerr]]
* [[User:Sannita (WMF)]] (I'm not sure if the right should be removed from a staff member, but they ''are'' inactive)
* [[User:Wooze]]
* <del>[[User:ZI Jony]]</del>
* [[User:Zippybonzo]]
* <del>[[User:沈澄心]]</del>
All of these users meet the threshold of inactivity on [[WF:Functioneer]]. Thanks, [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 18:56, 20 May 2026 (UTC)
:Hello @[[User:Feeglgeef|Feeglgeef]],
:I have stopped adding new functions, and I wouldn't mind if I'm removed from the functioneers list. If at any point I want it back, I'll request it. Thanks,
:Ege [[User:Egezort|Egezort]] ([[User talk:Egezort|talk]]) 19:00, 20 May 2026 (UTC)
:I have been inactive for some time, indeed. I just made an useful edit with intention to return at some point. [[User:Papuass|Papuass]] ([[User talk:Papuass|talk]]) 19:49, 20 May 2026 (UTC)
:I'm still interested in making new functions, but if there is a process for re-requesting access I don't mind requesting again. --[[User:Habst|Habst]] ([[User talk:Habst|talk]]) 20:35, 20 May 2026 (UTC)
::We're not a bureaucracy, so in my opinion just expressing interest in keeping the role is enough. I'll strike your name. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 20:36, 20 May 2026 (UTC)
:I'm still interested in project and I would like to keep the Functioneer rights, but don't have enough time at the moment. I hope that I'll can create thome new functions sporadically. --[[User:Butko|Butko]] ([[User talk:Butko|talk]]) 09:53, 21 May 2026 (UTC)
::Stricken. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 03:31, 25 May 2026 (UTC)
:I'm still interested in making new functions. Regards, [[User:ZI Jony|<span style="color:#8B0000">'''ZI Jony'''</span>]] [[User talk:ZI Jony|<sup><span style="color:Green"><i>(Talk)</i></span></sup>]] 17:15, 21 May 2026 (UTC)
:Am fine with it being removed, I was interested at one point in the concept of WF but when I rarely contribute to enwp I see no reason right now in keeping it around and will re-request if I gain interest again [[User:Zippybonzo|Zippybonzo]] ([[User talk:Zippybonzo|talk]]) 18:06, 21 May 2026 (UTC)
:I would like to keep the Functioneer rights, as they could be useful in future when someone sets up Wikifunctions for more Wikidata Lexeme Forms templates (see [[Wikifunctions:Projects using Wikifunctions]] and [[:d:Wikidata:Wikidata Lexeme Forms#Wikifunctions support]]). I’ve just made an edit on the [[Z10119|sandbox function]], maybe that suffices to technically fulfill the requirement. [[User:Lucas Werkmeister|Lucas Werkmeister]] ([[User talk:Lucas Werkmeister|talk]]) 21:23, 21 May 2026 (UTC)
::I've stricken your name. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 22:21, 21 May 2026 (UTC)
:You can remove my permissions as well, I will not have time to contribute regularly again until winter at the earliest. [[User:Autom|Autom]] ([[User talk:Autom|talk]]) 19:20, 22 May 2026 (UTC)
: I'm still interested in making new functions and implementations (e.g. [[Z34743]]). '''<span style="font-family:Iosevka,monospace">[[User:沈澄心|<span style="color:#9f3526">dring</span>]][[User talk:沈澄心|<span style="color:#534fa3">sim</span>]]</span>''' 10:38, 24 May 2026 (UTC)
:: I've stricken your name. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 14:10, 24 May 2026 (UTC)
: I do still plan to make new functions, particularly around transliteration from different pronunciation schemes into given target languages (and as long as discussions around abstract content architecture remain stagnant). [[User:Mahir256|Mahir256]] ([[User talk:Mahir256|talk]]) 18:48, 25 May 2026 (UTC)
== Import request ==
Please copy the hatnote template/module {{Q|5766677}} from a sister project. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 15:53, 5 June 2026 (UTC)
:Just out of curiosity: what is your intended use? [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 16:03, 5 June 2026 (UTC)
::Disambiguation on Project/Help pages. I've pre-emptively transcluded it on [[Special:EditPage/Wikifunctions:Catalogue/Programming functions|WF:Catalogue/Programming functions]] and [[Special:EditPage/Wikifunctions:Status|WF:Status]]. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 16:08, 5 June 2026 (UTC)
:Which one do you prefer? I can offer import from meta, commons, incubator, d, ar, en, es, fr, ru, zh. --[[User:Ameisenigel|Ameisenigel]] ([[User talk:Ameisenigel|talk]]) 20:46, 6 June 2026 (UTC)
::I'm not seeing a copy on Wikidata or Meta (or Incubator), and the copy on Commons doesn't seem to be globalised, so I guess enwp? I was hoping there was an existing globalised version somewhere. Maybe we can build NLG functions for hatnotes soon. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 22:42, 6 June 2026 (UTC)
:::I have imported the template and related modules. For some reason there is still an error "Lua error in package.lua at line 80: module 'Module:Hatnote list' not found." altough I have imported this as well. --[[User:Ameisenigel|Ameisenigel]] ([[User talk:Ameisenigel|talk]]) 17:55, 7 June 2026 (UTC)
== Navbar broken ==
{{#invoke:Navbar|navbar|WF:Administrators' noticeboard|mini=y|style=float: inline-end;}}
[[Module:Navbar]] doesn't seem to have been imported correctly. When invoking this module on this page, it works for reasons I don't understand, but on most pages like [[Project:Sandbox]] the necessary CSS rules for <code>.hlist li</code> etc. are all missing. {{Unsigned |1= YoshiRulz|2= 16:10, 5 June 2026}}
:@[[User:YoshiRulz|YoshiRulz]], I've fixed the issue! You were right, the problem was that the required CSS rules for <code>.hlist</code> and <code>.navbar</code> were missing on this wiki. To resolve this, I created [[Module:Navbar/styles.css]] and imported the necessary styles, then updated [[Module:Navbar]] to properly load this stylesheet. The navbar should now render correctly on all pages. Let me know if you still see any weird formatting. Regards, [[User:ZI Jony|<span style="color:#8B0000">'''ZI Jony'''</span>]] [[User talk:ZI Jony|<sup><span style="color:Green"><i>(Talk)</i></span></sup>]] 18:51, 5 June 2026 (UTC)
::Still not working on the Sandbox page. Are there deeper caches that [[Special:Purge]] isn't clearing? [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 15:13, 6 June 2026 (UTC)
== RGBA colour's type converters don't match Rational ==
See [[Talk:Z28579#Mismatching JS code representation]] for details. {{Unsigned |1= YoshiRulz|2= 17:38, 5 June 2026}}
:It [[Special:ListGroupRights|looks]] like this is something that only Wikifunctions staff can do. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 17:44, 6 June 2026 (UTC)
::Why? I thought "wikilambda-edit-converter" is the required user right and this is granted to "user". --[[User:Ameisenigel|Ameisenigel]] ([[User talk:Ameisenigel|talk]]) 20:52, 6 June 2026 (UTC)
:::Oh, looks like it is there. Either way, I get an error when I try. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 21:02, 6 June 2026 (UTC)
2a668y9am1h70128lt99qdh9jb2qa7z
281452
281444
2026-06-07T18:48:47Z
YoshiRulz
10156
/* Import request */ Reply
281452
wikitext
text/x-wiki
{{dynamite|title=Administrators' noticeboard|t=yes}}
{{Autoarchive resolved section
|age = 1
|archive = ((FULLPAGENAME))/Archive/((year))/((month:##))
|timeout=30
}}
{{Archives|{{Flatlist|{{Special:PrefixIndex/Wikifunctions:Administrators' noticeboard/Archive/|stripprefix=1}} }}}}
<!-- Add new reports below this line -->
== Inactive functioneers ==
* [[User:Autom]]
* <del>[[User:Butko]]</del>
* [[User:Egezort]]
* [[User:Elwinlhq]]
* <del>[[User:Habst]]</del>
* <del>[[User:Lucas Werkmeister]]</del>
* <del>[[User:Mahir256]]</del>
* <del>[[User:Papuass]]</del>
* [[User:Renamerr]]
* [[User:Sannita (WMF)]] (I'm not sure if the right should be removed from a staff member, but they ''are'' inactive)
* [[User:Wooze]]
* <del>[[User:ZI Jony]]</del>
* [[User:Zippybonzo]]
* <del>[[User:沈澄心]]</del>
All of these users meet the threshold of inactivity on [[WF:Functioneer]]. Thanks, [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 18:56, 20 May 2026 (UTC)
:Hello @[[User:Feeglgeef|Feeglgeef]],
:I have stopped adding new functions, and I wouldn't mind if I'm removed from the functioneers list. If at any point I want it back, I'll request it. Thanks,
:Ege [[User:Egezort|Egezort]] ([[User talk:Egezort|talk]]) 19:00, 20 May 2026 (UTC)
:I have been inactive for some time, indeed. I just made an useful edit with intention to return at some point. [[User:Papuass|Papuass]] ([[User talk:Papuass|talk]]) 19:49, 20 May 2026 (UTC)
:I'm still interested in making new functions, but if there is a process for re-requesting access I don't mind requesting again. --[[User:Habst|Habst]] ([[User talk:Habst|talk]]) 20:35, 20 May 2026 (UTC)
::We're not a bureaucracy, so in my opinion just expressing interest in keeping the role is enough. I'll strike your name. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 20:36, 20 May 2026 (UTC)
:I'm still interested in project and I would like to keep the Functioneer rights, but don't have enough time at the moment. I hope that I'll can create thome new functions sporadically. --[[User:Butko|Butko]] ([[User talk:Butko|talk]]) 09:53, 21 May 2026 (UTC)
::Stricken. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 03:31, 25 May 2026 (UTC)
:I'm still interested in making new functions. Regards, [[User:ZI Jony|<span style="color:#8B0000">'''ZI Jony'''</span>]] [[User talk:ZI Jony|<sup><span style="color:Green"><i>(Talk)</i></span></sup>]] 17:15, 21 May 2026 (UTC)
:Am fine with it being removed, I was interested at one point in the concept of WF but when I rarely contribute to enwp I see no reason right now in keeping it around and will re-request if I gain interest again [[User:Zippybonzo|Zippybonzo]] ([[User talk:Zippybonzo|talk]]) 18:06, 21 May 2026 (UTC)
:I would like to keep the Functioneer rights, as they could be useful in future when someone sets up Wikifunctions for more Wikidata Lexeme Forms templates (see [[Wikifunctions:Projects using Wikifunctions]] and [[:d:Wikidata:Wikidata Lexeme Forms#Wikifunctions support]]). I’ve just made an edit on the [[Z10119|sandbox function]], maybe that suffices to technically fulfill the requirement. [[User:Lucas Werkmeister|Lucas Werkmeister]] ([[User talk:Lucas Werkmeister|talk]]) 21:23, 21 May 2026 (UTC)
::I've stricken your name. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 22:21, 21 May 2026 (UTC)
:You can remove my permissions as well, I will not have time to contribute regularly again until winter at the earliest. [[User:Autom|Autom]] ([[User talk:Autom|talk]]) 19:20, 22 May 2026 (UTC)
: I'm still interested in making new functions and implementations (e.g. [[Z34743]]). '''<span style="font-family:Iosevka,monospace">[[User:沈澄心|<span style="color:#9f3526">dring</span>]][[User talk:沈澄心|<span style="color:#534fa3">sim</span>]]</span>''' 10:38, 24 May 2026 (UTC)
:: I've stricken your name. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 14:10, 24 May 2026 (UTC)
: I do still plan to make new functions, particularly around transliteration from different pronunciation schemes into given target languages (and as long as discussions around abstract content architecture remain stagnant). [[User:Mahir256|Mahir256]] ([[User talk:Mahir256|talk]]) 18:48, 25 May 2026 (UTC)
== Import request ==
Please copy the hatnote template/module {{Q|5766677}} from a sister project. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 15:53, 5 June 2026 (UTC)
:Just out of curiosity: what is your intended use? [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 16:03, 5 June 2026 (UTC)
::Disambiguation on Project/Help pages. I've pre-emptively transcluded it on [[Special:EditPage/Wikifunctions:Catalogue/Programming functions|WF:Catalogue/Programming functions]] and [[Special:EditPage/Wikifunctions:Status|WF:Status]]. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 16:08, 5 June 2026 (UTC)
:Which one do you prefer? I can offer import from meta, commons, incubator, d, ar, en, es, fr, ru, zh. --[[User:Ameisenigel|Ameisenigel]] ([[User talk:Ameisenigel|talk]]) 20:46, 6 June 2026 (UTC)
::I'm not seeing a copy on Wikidata or Meta (or Incubator), and the copy on Commons doesn't seem to be globalised, so I guess enwp? I was hoping there was an existing globalised version somewhere. Maybe we can build NLG functions for hatnotes soon. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 22:42, 6 June 2026 (UTC)
:::I have imported the template and related modules. For some reason there is still an error "Lua error in package.lua at line 80: module 'Module:Hatnote list' not found." altough I have imported this as well. --[[User:Ameisenigel|Ameisenigel]] ([[User talk:Ameisenigel|talk]]) 17:55, 7 June 2026 (UTC)
::::I see "[[Module:Pagetype]] not found" [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 18:48, 7 June 2026 (UTC)
== Navbar broken ==
{{#invoke:Navbar|navbar|WF:Administrators' noticeboard|mini=y|style=float: inline-end;}}
[[Module:Navbar]] doesn't seem to have been imported correctly. When invoking this module on this page, it works for reasons I don't understand, but on most pages like [[Project:Sandbox]] the necessary CSS rules for <code>.hlist li</code> etc. are all missing. {{Unsigned |1= YoshiRulz|2= 16:10, 5 June 2026}}
:@[[User:YoshiRulz|YoshiRulz]], I've fixed the issue! You were right, the problem was that the required CSS rules for <code>.hlist</code> and <code>.navbar</code> were missing on this wiki. To resolve this, I created [[Module:Navbar/styles.css]] and imported the necessary styles, then updated [[Module:Navbar]] to properly load this stylesheet. The navbar should now render correctly on all pages. Let me know if you still see any weird formatting. Regards, [[User:ZI Jony|<span style="color:#8B0000">'''ZI Jony'''</span>]] [[User talk:ZI Jony|<sup><span style="color:Green"><i>(Talk)</i></span></sup>]] 18:51, 5 June 2026 (UTC)
::Still not working on the Sandbox page. Are there deeper caches that [[Special:Purge]] isn't clearing? [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 15:13, 6 June 2026 (UTC)
== RGBA colour's type converters don't match Rational ==
See [[Talk:Z28579#Mismatching JS code representation]] for details. {{Unsigned |1= YoshiRulz|2= 17:38, 5 June 2026}}
:It [[Special:ListGroupRights|looks]] like this is something that only Wikifunctions staff can do. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 17:44, 6 June 2026 (UTC)
::Why? I thought "wikilambda-edit-converter" is the required user right and this is granted to "user". --[[User:Ameisenigel|Ameisenigel]] ([[User talk:Ameisenigel|talk]]) 20:52, 6 June 2026 (UTC)
:::Oh, looks like it is there. Either way, I get an error when I try. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 21:02, 6 June 2026 (UTC)
rugim1soj45o61z9xqebs6w4hx1w0bd
281621
281452
2026-06-07T19:34:03Z
Ameisenigel
44
/* Import request */ Reply
281621
wikitext
text/x-wiki
{{dynamite|title=Administrators' noticeboard|t=yes}}
{{Autoarchive resolved section
|age = 1
|archive = ((FULLPAGENAME))/Archive/((year))/((month:##))
|timeout=30
}}
{{Archives|{{Flatlist|{{Special:PrefixIndex/Wikifunctions:Administrators' noticeboard/Archive/|stripprefix=1}} }}}}
<!-- Add new reports below this line -->
== Inactive functioneers ==
* [[User:Autom]]
* <del>[[User:Butko]]</del>
* [[User:Egezort]]
* [[User:Elwinlhq]]
* <del>[[User:Habst]]</del>
* <del>[[User:Lucas Werkmeister]]</del>
* <del>[[User:Mahir256]]</del>
* <del>[[User:Papuass]]</del>
* [[User:Renamerr]]
* [[User:Sannita (WMF)]] (I'm not sure if the right should be removed from a staff member, but they ''are'' inactive)
* [[User:Wooze]]
* <del>[[User:ZI Jony]]</del>
* [[User:Zippybonzo]]
* <del>[[User:沈澄心]]</del>
All of these users meet the threshold of inactivity on [[WF:Functioneer]]. Thanks, [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 18:56, 20 May 2026 (UTC)
:Hello @[[User:Feeglgeef|Feeglgeef]],
:I have stopped adding new functions, and I wouldn't mind if I'm removed from the functioneers list. If at any point I want it back, I'll request it. Thanks,
:Ege [[User:Egezort|Egezort]] ([[User talk:Egezort|talk]]) 19:00, 20 May 2026 (UTC)
:I have been inactive for some time, indeed. I just made an useful edit with intention to return at some point. [[User:Papuass|Papuass]] ([[User talk:Papuass|talk]]) 19:49, 20 May 2026 (UTC)
:I'm still interested in making new functions, but if there is a process for re-requesting access I don't mind requesting again. --[[User:Habst|Habst]] ([[User talk:Habst|talk]]) 20:35, 20 May 2026 (UTC)
::We're not a bureaucracy, so in my opinion just expressing interest in keeping the role is enough. I'll strike your name. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 20:36, 20 May 2026 (UTC)
:I'm still interested in project and I would like to keep the Functioneer rights, but don't have enough time at the moment. I hope that I'll can create thome new functions sporadically. --[[User:Butko|Butko]] ([[User talk:Butko|talk]]) 09:53, 21 May 2026 (UTC)
::Stricken. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 03:31, 25 May 2026 (UTC)
:I'm still interested in making new functions. Regards, [[User:ZI Jony|<span style="color:#8B0000">'''ZI Jony'''</span>]] [[User talk:ZI Jony|<sup><span style="color:Green"><i>(Talk)</i></span></sup>]] 17:15, 21 May 2026 (UTC)
:Am fine with it being removed, I was interested at one point in the concept of WF but when I rarely contribute to enwp I see no reason right now in keeping it around and will re-request if I gain interest again [[User:Zippybonzo|Zippybonzo]] ([[User talk:Zippybonzo|talk]]) 18:06, 21 May 2026 (UTC)
:I would like to keep the Functioneer rights, as they could be useful in future when someone sets up Wikifunctions for more Wikidata Lexeme Forms templates (see [[Wikifunctions:Projects using Wikifunctions]] and [[:d:Wikidata:Wikidata Lexeme Forms#Wikifunctions support]]). I’ve just made an edit on the [[Z10119|sandbox function]], maybe that suffices to technically fulfill the requirement. [[User:Lucas Werkmeister|Lucas Werkmeister]] ([[User talk:Lucas Werkmeister|talk]]) 21:23, 21 May 2026 (UTC)
::I've stricken your name. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 22:21, 21 May 2026 (UTC)
:You can remove my permissions as well, I will not have time to contribute regularly again until winter at the earliest. [[User:Autom|Autom]] ([[User talk:Autom|talk]]) 19:20, 22 May 2026 (UTC)
: I'm still interested in making new functions and implementations (e.g. [[Z34743]]). '''<span style="font-family:Iosevka,monospace">[[User:沈澄心|<span style="color:#9f3526">dring</span>]][[User talk:沈澄心|<span style="color:#534fa3">sim</span>]]</span>''' 10:38, 24 May 2026 (UTC)
:: I've stricken your name. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 14:10, 24 May 2026 (UTC)
: I do still plan to make new functions, particularly around transliteration from different pronunciation schemes into given target languages (and as long as discussions around abstract content architecture remain stagnant). [[User:Mahir256|Mahir256]] ([[User talk:Mahir256|talk]]) 18:48, 25 May 2026 (UTC)
== Import request ==
Please copy the hatnote template/module {{Q|5766677}} from a sister project. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 15:53, 5 June 2026 (UTC)
:Just out of curiosity: what is your intended use? [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 16:03, 5 June 2026 (UTC)
::Disambiguation on Project/Help pages. I've pre-emptively transcluded it on [[Special:EditPage/Wikifunctions:Catalogue/Programming functions|WF:Catalogue/Programming functions]] and [[Special:EditPage/Wikifunctions:Status|WF:Status]]. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 16:08, 5 June 2026 (UTC)
:Which one do you prefer? I can offer import from meta, commons, incubator, d, ar, en, es, fr, ru, zh. --[[User:Ameisenigel|Ameisenigel]] ([[User talk:Ameisenigel|talk]]) 20:46, 6 June 2026 (UTC)
::I'm not seeing a copy on Wikidata or Meta (or Incubator), and the copy on Commons doesn't seem to be globalised, so I guess enwp? I was hoping there was an existing globalised version somewhere. Maybe we can build NLG functions for hatnotes soon. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 22:42, 6 June 2026 (UTC)
:::I have imported the template and related modules. For some reason there is still an error "Lua error in package.lua at line 80: module 'Module:Hatnote list' not found." altough I have imported this as well. --[[User:Ameisenigel|Ameisenigel]] ([[User talk:Ameisenigel|talk]]) 17:55, 7 June 2026 (UTC)
::::I see "[[Module:Pagetype]] not found" [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 18:48, 7 June 2026 (UTC)
:::::I have imported this one and a few others. However, some modules will need to be adapted because of the differences between the wikis. --[[User:Ameisenigel|Ameisenigel]] ([[User talk:Ameisenigel|talk]]) 19:34, 7 June 2026 (UTC)
== Navbar broken ==
{{#invoke:Navbar|navbar|WF:Administrators' noticeboard|mini=y|style=float: inline-end;}}
[[Module:Navbar]] doesn't seem to have been imported correctly. When invoking this module on this page, it works for reasons I don't understand, but on most pages like [[Project:Sandbox]] the necessary CSS rules for <code>.hlist li</code> etc. are all missing. {{Unsigned |1= YoshiRulz|2= 16:10, 5 June 2026}}
:@[[User:YoshiRulz|YoshiRulz]], I've fixed the issue! You were right, the problem was that the required CSS rules for <code>.hlist</code> and <code>.navbar</code> were missing on this wiki. To resolve this, I created [[Module:Navbar/styles.css]] and imported the necessary styles, then updated [[Module:Navbar]] to properly load this stylesheet. The navbar should now render correctly on all pages. Let me know if you still see any weird formatting. Regards, [[User:ZI Jony|<span style="color:#8B0000">'''ZI Jony'''</span>]] [[User talk:ZI Jony|<sup><span style="color:Green"><i>(Talk)</i></span></sup>]] 18:51, 5 June 2026 (UTC)
::Still not working on the Sandbox page. Are there deeper caches that [[Special:Purge]] isn't clearing? [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 15:13, 6 June 2026 (UTC)
== RGBA colour's type converters don't match Rational ==
See [[Talk:Z28579#Mismatching JS code representation]] for details. {{Unsigned |1= YoshiRulz|2= 17:38, 5 June 2026}}
:It [[Special:ListGroupRights|looks]] like this is something that only Wikifunctions staff can do. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 17:44, 6 June 2026 (UTC)
::Why? I thought "wikilambda-edit-converter" is the required user right and this is granted to "user". --[[User:Ameisenigel|Ameisenigel]] ([[User talk:Ameisenigel|talk]]) 20:52, 6 June 2026 (UTC)
:::Oh, looks like it is there. Either way, I get an error when I try. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 21:02, 6 June 2026 (UTC)
q1a0r10gwnfbnvpdt02ho4n385t4t70
Z14302
0
26555
281622
262355
2026-06-07T20:15:45Z
Comfyquiettree
6942
adding czech
281622
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z14302"
},
"Z2K2": {
"Z1K1": "Z14294",
"Z14294K1": [
"Z14293",
{
"Z1K1": "Z14293",
"Z14293K1": "Z14295",
"Z14293K2": [
"Z60",
"Z1002",
"Z1113",
"Z1199",
"Z1124",
"Z1689",
"Z1830"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z14364",
"Z14293K2": [
"Z60",
"Z1021",
"Z1003",
"Z1430",
"Z1037",
"Z1789",
"Z1158",
"Z1061"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z14368",
"Z14293K2": [
"Z60",
"Z1051",
"Z1004",
"Z1640"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z14435",
"Z14293K2": [
"Z60"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z14438",
"Z14293K2": [
"Z60",
"Z1592",
"Z1025",
"Z1787",
"Z1005",
"Z1062"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z19503",
"Z14293K2": [
"Z60",
"Z1012"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z11022",
"Z14293K2": [
"Z60",
"Z1403"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z23283",
"Z14293K2": [
"Z60",
"Z1966"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z23344",
"Z14293K2": [
"Z60",
"Z1820",
"Z1569",
"Z1749",
"Z1206"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z23843",
"Z14293K2": [
"Z60",
"Z1429"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z28422",
"Z14293K2": [
"Z60",
"Z1011",
"Z1197",
"Z1148"
]
}
],
"Z14294K2": "Z13713"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "config for displaying Natural number"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1011",
"Z11K2": "স্বাভাবিক সংখ্যার জন্য ফাংশন প্রদর্শন"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1014",
"Z11K2": "ọrụ ngosi maka ọnụọgụ eke"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1592",
"Z11K2": "visningsfunktioner för naturliga tal"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1004",
"Z11K2": "fonctions d'affichage pour les nombres naturels"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Anzeigefunktionen für natürliche Zahl"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1787",
"Z11K2": "funzioni di stampa dei numeri naturali"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1394",
"Z11K2": "Funkcije za prikaz prirodnih brojeva"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1181",
"Z11K2": "функције за приказ природних бројева"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"Natural number display functions",
"Renderers for natural numbers",
"display functions for natural number"
]
},
{
"Z1K1": "Z31",
"Z31K1": "Z1011",
"Z31K2": [
"Z6",
"স্বাভাবিক সংখ্যার প্রদর্শন ফাংশন"
]
},
{
"Z1K1": "Z31",
"Z31K1": "Z1787",
"Z31K2": [
"Z6",
"formattatori di numeri naturali"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
h0zx07a5l9qu2flts36wxyt3keb1j9u
Z15080
0
28231
281447
281126
2026-06-07T18:43:37Z
WikiLambda system
3
Updated the implementation list (see [[Help:Wikifunctions/Implementation_ordering_and_choosing|About implementation selection]])
281447
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z15080"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z13518",
"Z17K2": "Z15080K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "n"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1014",
"Z11K2": "n"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "n"
}
]
}
}
],
"Z8K2": "Z13518",
"Z8K3": [
"Z20",
"Z15082",
"Z15083",
"Z15084"
],
"Z8K4": [
"Z14",
"Z15081",
"Z34518"
],
"Z8K5": "Z15080"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Perrin number"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1014",
"Z11K2": "Nọmba Perrin"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Perrin-Zahl"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"A001608"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
8fb0bea6iyqfyd21kvekyqzt3uls8na
User:Jsamwrites
2
39114
281207
272627
2026-06-07T12:19:03Z
Jsamwrites
938
/* Sentences */
281207
wikitext
text/x-wiki
== Introduction ==
This is the Wikifunctions user page of John Samuel. For more information, refer page on [[:d:User:Jsamwrites|Wikidata User:Jsamwrites]].
== Phrases ==
List of objects:
* {{Z|Z18928}}
* {{Z|Z18929}}
* {{Z|Z18979}}
== Function application ==
* {{Z|Z873}}
== Configuration ==
* {{Z|Z14294}}
== Types ==
* [[Special:ListObjectsByType|All supported types]]
== Wikidata ==
* {{Z|Z22220}}
* {{Z|Z22222}}
== Sentences ==
{| class="wikitable"
|+
!No.
!Function
!Configuration
!Example
|-
|1.
|{{Z|Z34282}}
|{{Z|Z34281}}
|Lyon is a commune of France
|-
|2.
|{{Z|Z32581}}
|{{Z|Z32534}}
|Mona Lisa is a painting by Leonardo da Vinci
|-
|3.
|{{Z|Z34637}}
|{{Z|Z34682}}
|Sunday is part of the weekend
|-
|4.
|{{Z|Z36141}}
|{{Z|Z36148}}
|Sunday is part of the weekend
|-
|5.
|{{Z|Z36151}}
|{{Z|Z36155}}
|Eiffel Tower was built between 1887 and 1889.
|}
=== References ===
* {{Z|Z31921}}
== Catalogue ==
* [[Wikifunctions:Catalogue|Catalogue of important functions]]
* [[Wikifunctions:Catalogue/Natural language operations/Global language functions|Global language functions]]
== Documentation ==
* [[Wikifunctions:Catalogue/Wikidata operations|Wikidata operations]]
a9i8ju66ukneovpskymu82c7upaduj6
281208
281207
2026-06-07T12:20:06Z
Jsamwrites
938
/* Sentences */
281208
wikitext
text/x-wiki
== Introduction ==
This is the Wikifunctions user page of John Samuel. For more information, refer page on [[:d:User:Jsamwrites|Wikidata User:Jsamwrites]].
== Phrases ==
List of objects:
* {{Z|Z18928}}
* {{Z|Z18929}}
* {{Z|Z18979}}
== Function application ==
* {{Z|Z873}}
== Configuration ==
* {{Z|Z14294}}
== Types ==
* [[Special:ListObjectsByType|All supported types]]
== Wikidata ==
* {{Z|Z22220}}
* {{Z|Z22222}}
== Sentences ==
{| class="wikitable"
|+
!No.
!Function
!Configuration
!Example
|-
|1.
|{{Z|Z34282}}
|{{Z|Z34281}}
|Lyon is a commune of France
|-
|2.
|{{Z|Z32581}}
|{{Z|Z32534}}
|Mona Lisa is a painting by Leonardo da Vinci
|-
|3.
|{{Z|Z34637}}
|{{Z|Z34682}}
|Sunday is part of the weekend
|-
|4.
|{{Z|Z36141}}
|{{Z|Z36148}}
|Space Needle was built in 1961.
|-
|5.
|{{Z|Z36151}}
|{{Z|Z36155}}
|Eiffel Tower was built between 1887 and 1889.
|}
=== References ===
* {{Z|Z31921}}
== Catalogue ==
* [[Wikifunctions:Catalogue|Catalogue of important functions]]
* [[Wikifunctions:Catalogue/Natural language operations/Global language functions|Global language functions]]
== Documentation ==
* [[Wikifunctions:Catalogue/Wikidata operations|Wikidata operations]]
tkz5cz8imp1n18kyqd7e7fwl3ic99j2
Z19124
0
40758
281477
132271
2026-06-07T19:20:30Z
Ameisenigel
44
de
281477
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z19124"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z10070",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z10070",
"Z10070K1": "Andiamo tutti a casa di Pippo",
"Z10070K2": "tti a cas"
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z844",
"Z844K2": {
"Z1K1": "Z40",
"Z40K1": "Z41"
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1787",
"Z11K2": "controllo di parole intere"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "\"tti a cas\" existiert in Zeichenkette"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1787",
"Z11K2": "sottostringa esistente: 'Andiamo tutti a casa di Pippo' e 'tti a casa'"
}
]
}
}
n8uwzdqkyqa5c3p1wcxubun464h7jyh
Z19125
0
40762
281478
132654
2026-06-07T19:21:12Z
Ameisenigel
44
e.g. ist nicht deutsch
281478
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z19125"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z6",
"Z17K2": "Z19125K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "lemma"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1004",
"Z11K2": "lemme"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Lemma"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1272",
"Z11K2": "lema"
}
]
}
}
],
"Z8K2": "Z6",
"Z8K3": [
"Z20",
"Z19126",
"Z19128",
"Z19131",
"Z19127",
"Z19130"
],
"Z8K4": [
"Z14",
"Z19129"
],
"Z8K5": "Z19125"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "English plural possessive"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1004",
"Z11K2": "possessif pluriel en anglais"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1282",
"Z11K2": "liester perc'hennañ e saozneg"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Englisch Plural Possessiv"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1272",
"Z11K2": "Engleska množina u gentivu"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1001",
"Z11K2": "الإنجليزية، صيغة الملكية في الجمع"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"English plural genitive"
]
},
{
"Z1K1": "Z31",
"Z31K1": "Z1004",
"Z31K2": [
"Z6",
"génitif pluriel en anglais"
]
},
{
"Z1K1": "Z31",
"Z31K1": "Z1430",
"Z31K2": [
"Z6",
"English Plural Genetiv"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Returns the form for a plural possessive or genitive based on the lemma form, e.g. dog → dogs'"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1004",
"Z11K2": "renvoie la forme possessive (ou génitive) du pluriel en anglais en se basant sur la forme du lemme, par ex. dog → dogs'"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Gibt die Plural-possessiv-Form des gegebenen Lemma zurück, z. B. dog → dogs'"
}
]
}
}
qkb8py08uzsz83afk07q69ad8qv17ao
Z19126
0
40763
281479
132283
2026-06-07T19:21:36Z
Ameisenigel
44
de
281479
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z19126"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z19125",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z19125",
"Z19125K1": "dog"
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z866",
"Z866K2": "dogs'"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "dog → dogs'"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "dog → dogs'"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
72k0ez7k89nrn6jetxthnjze4f5n8f7
Z19127
0
40764
281480
132652
2026-06-07T19:23:23Z
Ameisenigel
44
de
281480
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z19127"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z19125",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z19125",
"Z19125K1": "fish"
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z13381",
"Z13381K2": [
"Z6",
"fishes'",
"fish's"
]
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "fish → fish's or fishes'"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "fish → fishes, fish's"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "the plural of “fish” is either “fish” or “fishes” and the corresponding possessive forms are “fish's” and “fishes'”"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Plural von \"fish\" ist entweder \"fish\" oder \"fishes\", damit ergeben sich die Possessivformen \"fish's\" und \"fishes\""
}
]
}
}
pqjh49a1nl0d1kifpkrdiv4ryvyc32k
Z19128
0
40765
281481
132284
2026-06-07T19:23:45Z
Ameisenigel
44
de
281481
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z19128"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z19125",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z19125",
"Z19125K1": "volunteer"
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z866",
"Z866K2": "volunteers'"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "volunteer → volunteers'"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "volunteer → volunteers'"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
cyh9ff5jq10tyh2z14r78ii2swdgk5l
Z19129
0
40766
281482
132272
2026-06-07T19:24:29Z
Ameisenigel
44
de
281482
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z19129"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z19125",
"Z14K2": {
"Z1K1": "Z7",
"Z7K1": "Z11302",
"Z11302K1": {
"Z1K1": "Z7",
"Z7K1": "Z11089",
"Z11089K1": {
"Z1K1": "Z18",
"Z18K1": "Z19125K1"
}
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "composition of plural and possessive"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Englisch Plural Possessiv als Komposition"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
2d1a65gtmw1fny7uuc5vi7y1altfp38
Z19130
0
40767
281483
132653
2026-06-07T19:24:56Z
Ameisenigel
44
de
281483
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z19130"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z19125",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z19125",
"Z19125K1": "Matrix"
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z13381",
"Z13381K2": [
"Z6",
"Matrices'",
"Matrixes'"
]
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Matrix → Matrices' or Matrixes'"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Matrix → Matrices', Matrixes'"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
886j7exlts9hnkkz052vni8nv0wtce2
Wikifunctions:Catalogue/Programming functions
4
41964
281623
269653
2026-06-07T20:35:38Z
YoshiRulz
10156
Revise hatnote
281623
wikitext
text/x-wiki
{{about|the [[Z61|programming languages]] available in {{SITENAME}}
|more information about Implementations-by-code|WF:PROG
|comparisons between {{SITENAME}} and other programming environments, or a "rosetta"-style function reference|WF:Rosetta
}}
Functions that are directly related to programming languages.
* {{Z+|Z23312}}
* {{Z+|Z18722}}
* {{Z+|Z13871}}
[[Category:Lists of functions]]
60n3mtegvq97epv2l7xqrr955jr8y1f
Z23934
0
54297
281215
278370
2026-06-07T12:48:41Z
WikiLambda system
3
Updated the implementation list (see [[Help:Wikifunctions/Implementation_ordering_and_choosing|About implementation selection]])
281215
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z23934"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z19677",
"Z17K2": "Z23934K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "rational"
}
]
}
}
],
"Z8K2": "Z19677",
"Z8K3": [
"Z20",
"Z23936",
"Z24327"
],
"Z8K4": [
"Z14",
"Z35440",
"Z23935"
],
"Z8K5": "Z23934"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "non-integer part of rational"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"fractional part of Rational"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
41maywkppu3ozawf8r0zhliscnc0tj9
Z26570
0
61686
281209
280931
2026-06-07T12:27:13Z
WikiLambda system
3
Updated the implementation list (see [[Help:Wikifunctions/Implementation_ordering_and_choosing|About implementation selection]])
281209
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z26570"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z6091",
"Z17K2": "Z26570K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "entity"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1643",
"Z11K2": "클래스"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1004",
"Z11K2": "entité"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1078",
"Z11K2": "entitas"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1062",
"Z11K2": "entita"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1645",
"Z11K2": "实体"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1592",
"Z11K2": "entitet"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Ding"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z6091",
"Z17K2": "Z26570K2",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "class"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1643",
"Z11K2": "엔터티"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1004",
"Z11K2": "classe"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1078",
"Z11K2": "kelas"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1062",
"Z11K2": "třída"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1645",
"Z11K2": "类别"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1592",
"Z11K2": "typ"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Art"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z6091",
"Z17K2": "Z26570K3",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "location"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1643",
"Z11K2": "위치"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1004",
"Z11K2": "localisation"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1078",
"Z11K2": "lokasi"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1062",
"Z11K2": "umístění"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1645",
"Z11K2": "位置"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1592",
"Z11K2": "plats"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Ort"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z60",
"Z17K2": "Z26570K4",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1643",
"Z11K2": "언어"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "language"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1004",
"Z11K2": "langue"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1078",
"Z11K2": "bahasa"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1062",
"Z11K2": "jazyk"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1645",
"Z11K2": "语言"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1592",
"Z11K2": "språk"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Sprache"
}
]
}
}
],
"Z8K2": "Z11",
"Z8K3": [
"Z20",
"Z26609",
"Z26623",
"Z26625",
"Z26626",
"Z26932",
"Z27175",
"Z27176",
"Z32289",
"Z32377",
"Z32861",
"Z35621",
"Z35620",
"Z35619",
"Z35622",
"Z35623",
"Z35624",
"Z35625",
"Z36121"
],
"Z8K4": [
"Z14",
"Z29840",
"Z34043"
],
"Z8K5": "Z26570"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "state location using entity and class"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1643",
"Z11K2": "엔터티와 클래스를 사용하여 위치 지정"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1004",
"Z11K2": "localiser en utilisant l'entité et la classe"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1078",
"Z11K2": "Berikan lokasi menggunakan entitas dan kelas"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1062",
"Z11K2": "vyjádřit umístění pomocí entity a třídy"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1645",
"Z11K2": "使用实体和类别说明位置"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1592",
"Z11K2": "Ange plats med entitet och typ"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "Satz über ein Ding einer Art an einem Ort"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"something is a something in somewhere",
"is a ? in ?",
"location is a class in entity",
"X is a Y in Z"
]
},
{
"Z1K1": "Z31",
"Z31K1": "Z1078",
"Z31K2": [
"Z6",
"sesuatu adalah sesuatu di suatu tempat"
]
},
{
"Z1K1": "Z31",
"Z31K1": "Z1645",
"Z31K2": [
"Z6",
"某物是某地的某类事物"
]
},
{
"Z1K1": "Z31",
"Z31K1": "Z1592",
"Z31K2": [
"Z6",
"Ange plats med entitet och klass"
]
},
{
"Z1K1": "Z31",
"Z31K1": "Z1430",
"Z31K2": [
"Z6",
"X ist ein Y in Z"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Forms a sentence stating the location and class of a given entity. E.g. \"Seoul is a city in South Korea.\""
},
{
"Z1K1": "Z11",
"Z11K1": "Z1078",
"Z11K2": "Membuat kalimat yang memberikan lokasi dan kelas dari entitas yang diberikan. Misalnya \"Seoul adalah kota di Korea Selatan.\""
},
{
"Z1K1": "Z11",
"Z11K1": "Z1062",
"Z11K2": "Vytvoří větu popisující polohu a třídu dané entity. Např. „Soul je město v Jižní Koreji.“"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1645",
"Z11K2": "生成一句话,说明某个给定实体的类别及其所在位置。如:“首尔是韩国的一座城市。”"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1592",
"Z11K2": "Skapar en mening med plats och typ av en angiven entitet. Ex. \"Seoul är en stad i Sydkorea.\""
},
{
"Z1K1": "Z11",
"Z11K1": "Z1430",
"Z11K2": "z.B. \"Seoul ist eine Stadt in Südkorea.\""
}
]
}
}
mlyk0j3eoxbyeodn9ppfhd52p6omuzi
Z31093
0
72675
281643
242202
2026-06-07T23:49:24Z
YoshiRulz
10156
Added Z36179 to the approved list of implementations
281643
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z31093"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z20838"
},
"Z17K2": "Z31093K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "values to check"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z20838"
},
"Z17K2": "Z31093K2",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "reference values"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z20838",
"Z17K2": "Z31093K3",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "epsilon"
}
]
}
}
],
"Z8K2": "Z40",
"Z8K3": [
"Z20",
"Z31094"
],
"Z8K4": [
"Z14",
"Z31097",
"Z36179"
],
"Z8K5": "Z31093"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "same list of float64s within tolerance"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"equality of float64 lists within tolerance",
"equivalent list of float64s within tolerance",
"same list of float64s within error",
"same list of float64s within epsilon"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
86v516d3pmn01csfycqlut8dh9hae9e
Z31097
0
72679
281644
242200
2026-06-07T23:50:08Z
YoshiRulz
10156
Disambiguate en label
281644
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z31097"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z31093",
"Z14K2": {
"Z1K1": "Z7",
"Z7K1": "Z12684",
"Z12684K1": {
"Z1K1": "Z7",
"Z7K1": "Z31095",
"Z31095K1": "Z31090",
"Z31095K2": {
"Z1K1": "Z18",
"Z18K1": "Z31093K1"
},
"Z31095K3": {
"Z1K1": "Z18",
"Z18K1": "Z31093K2"
},
"Z31095K4": {
"Z1K1": "Z18",
"Z18K1": "Z31093K3"
}
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "same float64s within tolerance, map composition"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
q6onuv92rbg1oyuxlyxeckdmqx64cav
User:YoshiRulz/Functions that are maybe done
2
73450
281654
279864
2026-06-08T00:19:57Z
YoshiRulz
10156
Add Z36174
281654
wikitext
text/x-wiki
{{phab|T412955}}
<br clear="all" /><hr>
=== {{Z|28217}} ===
tests timing out; connected impl. anyway
* {{Z|28218}}—[https://www.wikifunctions.org/wiki/Z28787?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z28787%22%2C%22Z28787K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13397%22%2C%22Z13397K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22978%22%2C%22Z22978K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6821%22%2C%22Z6821K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q19317%22%7D%7D%2C%22Z22978K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P527%22%7D%7D%2C%22Z13397K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z28755%22%2C%22Z28755K1%22%3A%7B%22Z1K1%22%3A%22Z20159%22%2C%22Z20159K1%22%3A%7B%22Z1K1%22%3A%22Z17813%22%2C%22Z17813K1%22%3A%22Z17814%22%7D%2C%22Z20159K2%22%3A%7B%22Z1K1%22%3A%22Z13518%22%2C%22Z13518K1%22%3A%221966%22%7D%7D%2C%22Z28755K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z873%22%2C%22Z873K1%22%3A%22Z28773%22%2C%22Z873K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z873%22%2C%22Z873K1%22%3A%22Z19308%22%2C%22Z873K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z22839%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z29694%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30172%22%2C%22Z30172K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30248%22%2C%22Z30248K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22978%22%2C%22Z22978K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6821%22%2C%22Z6821K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q19317%22%7D%7D%2C%22Z22978K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P527%22%7D%7D%2C%22Z30248K2%22%3A%5B%22Z6030%22%2C%7B%22Z1K1%22%3A%22Z6030%22%2C%22Z6030K1%22%3A%22Z6036%22%7D%5D%2C%22Z30248K3%22%3A%5B%22Z60%22%5D%2C%22Z30248K4%22%3A%5B%22Z6092%22%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P1346%22%7D%5D%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z23723%22%7D%7D%7D%7D%7D%7D%2C%22Z28787K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P1346%22%7D%7D full call]
** [https://www.wikifunctions.org/wiki/Z28755?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z28755%22%2C%22Z28755K1%22%3A%7B%22Z1K1%22%3A%22Z20159%22%2C%22Z20159K1%22%3A%7B%22Z1K1%22%3A%22Z17813%22%2C%22Z17813K1%22%3A%22Z17814%22%7D%2C%22Z20159K2%22%3A%7B%22Z1K1%22%3A%22Z13518%22%2C%22Z13518K1%22%3A%221966%22%7D%7D%2C%22Z28755K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z873%22%2C%22Z873K1%22%3A%22Z28773%22%2C%22Z873K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z873%22%2C%22Z873K1%22%3A%22Z19308%22%2C%22Z873K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z22839%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z29694%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30172%22%2C%22Z30172K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30248%22%2C%22Z30248K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22978%22%2C%22Z22978K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6821%22%2C%22Z6821K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q19317%22%7D%7D%2C%22Z22978K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P527%22%7D%7D%2C%22Z30248K2%22%3A%5B%22Z6030%22%2C%7B%22Z1K1%22%3A%22Z6030%22%2C%22Z6030K1%22%3A%22Z6036%22%7D%5D%2C%22Z30248K3%22%3A%5B%22Z60%22%5D%2C%22Z30248K4%22%3A%5B%22Z6092%22%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P1346%22%7D%5D%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z23723%22%7D%7D%7D%7D%7D index]
*** [https://www.wikifunctions.org/wiki/Z13464?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z22839%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z29694%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30172%22%2C%22Z30172K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30248%22%2C%22Z30248K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22978%22%2C%22Z22978K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6821%22%2C%22Z6821K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q19317%22%7D%7D%2C%22Z22978K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P527%22%7D%7D%2C%22Z30248K2%22%3A%5B%22Z6030%22%2C%7B%22Z1K1%22%3A%22Z6030%22%2C%22Z6030K1%22%3A%22Z6036%22%7D%5D%2C%22Z30248K3%22%3A%5B%22Z60%22%5D%2C%22Z30248K4%22%3A%5B%22Z6092%22%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P1346%22%7D%5D%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z23723%22%7D%7D singular P585s]
**** [https://www.wikifunctions.org/wiki/Z30248?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30248%22%2C%22Z30248K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22978%22%2C%22Z22978K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6821%22%2C%22Z6821K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q19317%22%7D%7D%2C%22Z22978K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P527%22%7D%7D%2C%22Z30248K2%22%3A%5B%22Z6030%22%2C%7B%22Z1K1%22%3A%22Z6030%22%2C%22Z6030K1%22%3A%22Z6036%22%7D%5D%2C%22Z30248K3%22%3A%5B%22Z60%22%5D%2C%22Z30248K4%22%3A%5B%22Z6092%22%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P1346%22%7D%5D%7D items]
* {{Z|31140}}—[https://www.wikifunctions.org/wiki/Z28787?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z28787%22%2C%22Z28787K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13397%22%2C%22Z13397K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22978%22%2C%22Z22978K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6821%22%2C%22Z6821K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q1317446%22%7D%7D%2C%22Z22978K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P527%22%7D%7D%2C%22Z13397K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z28755%22%2C%22Z28755K1%22%3A%7B%22Z1K1%22%3A%22Z20159%22%2C%22Z20159K1%22%3A%7B%22Z1K1%22%3A%22Z17813%22%2C%22Z17813K1%22%3A%22Z17814%22%7D%2C%22Z20159K2%22%3A%7B%22Z1K1%22%3A%22Z13518%22%2C%22Z13518K1%22%3A%221995%22%7D%7D%2C%22Z28755K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z873%22%2C%22Z873K1%22%3A%22Z28773%22%2C%22Z873K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z873%22%2C%22Z873K1%22%3A%22Z19308%22%2C%22Z873K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z22839%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z29694%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30172%22%2C%22Z30172K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30248%22%2C%22Z30248K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22978%22%2C%22Z22978K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6821%22%2C%22Z6821K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q1317446%22%7D%7D%2C%22Z22978K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P527%22%7D%7D%2C%22Z30248K2%22%3A%5B%22Z6030%22%2C%7B%22Z1K1%22%3A%22Z6030%22%2C%22Z6030K1%22%3A%22Z6036%22%7D%5D%2C%22Z30248K3%22%3A%5B%22Z60%22%5D%2C%22Z30248K4%22%3A%5B%22Z6092%22%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P1346%22%7D%5D%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z23723%22%7D%7D%7D%7D%7D%7D%2C%22Z28787K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P1346%22%7D%7D full call]
** [https://www.wikifunctions.org/wiki/Z28755?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z28755%22%2C%22Z28755K1%22%3A%7B%22Z1K1%22%3A%22Z20159%22%2C%22Z20159K1%22%3A%7B%22Z1K1%22%3A%22Z17813%22%2C%22Z17813K1%22%3A%22Z17814%22%7D%2C%22Z20159K2%22%3A%7B%22Z1K1%22%3A%22Z13518%22%2C%22Z13518K1%22%3A%221995%22%7D%7D%2C%22Z28755K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z873%22%2C%22Z873K1%22%3A%22Z28773%22%2C%22Z873K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z873%22%2C%22Z873K1%22%3A%22Z19308%22%2C%22Z873K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z22839%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z29694%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30172%22%2C%22Z30172K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30248%22%2C%22Z30248K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22978%22%2C%22Z22978K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6821%22%2C%22Z6821K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q1317446%22%7D%7D%2C%22Z22978K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P527%22%7D%7D%2C%22Z30248K2%22%3A%5B%22Z6030%22%2C%7B%22Z1K1%22%3A%22Z6030%22%2C%22Z6030K1%22%3A%22Z6036%22%7D%5D%2C%22Z30248K3%22%3A%5B%22Z60%22%5D%2C%22Z30248K4%22%3A%5B%22Z6092%22%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P1346%22%7D%5D%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z23723%22%7D%7D%7D%7D%7D index]
*** [https://www.wikifunctions.org/wiki/Z13464?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z22839%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z13464%22%2C%22Z13464K1%22%3A%22Z29694%22%2C%22Z13464K2%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30172%22%2C%22Z30172K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30248%22%2C%22Z30248K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22978%22%2C%22Z22978K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6821%22%2C%22Z6821K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q1317446%22%7D%7D%2C%22Z22978K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P527%22%7D%7D%2C%22Z30248K2%22%3A%5B%22Z6030%22%2C%7B%22Z1K1%22%3A%22Z6030%22%2C%22Z6030K1%22%3A%22Z6036%22%7D%5D%2C%22Z30248K3%22%3A%5B%22Z60%22%5D%2C%22Z30248K4%22%3A%5B%22Z6092%22%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P1346%22%7D%5D%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%7D%2C%22Z13464K3%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z23723%22%7D%7D singular P585s]
**** [https://www.wikifunctions.org/wiki/Z30248?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z30248%22%2C%22Z30248K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z22978%22%2C%22Z22978K1%22%3A%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6821%22%2C%22Z6821K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q1317446%22%7D%7D%2C%22Z22978K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P527%22%7D%7D%2C%22Z30248K2%22%3A%5B%22Z6030%22%2C%7B%22Z1K1%22%3A%22Z6030%22%2C%22Z6030K1%22%3A%22Z6036%22%7D%5D%2C%22Z30248K3%22%3A%5B%22Z60%22%5D%2C%22Z30248K4%22%3A%5B%22Z6092%22%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P585%22%7D%2C%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P1346%22%7D%5D%7D items]
=== {{Z|30931}} ===
tests timing out
* {{Z|30932}}
* {{Z|30933}}
* {{Z|30942}}
* {{Z|30947}} passing; trivial
=== {{Z|25052}} ===
test timing out
* {{Z|25053}}
=== {{Z|25054}} ===
test timing out
* {{Z|25055}} passing
* {{Z|25056}}
=== {{Z|29953}} ===
tests timing out
* {{Z|29954}}
* {{Z|29955}} expected failure
* {{Z|29956}}
=== {{Z|29784}} ===
tests timing out
* {{Z|30088}}
* {{Z|30915}}
=== {{Z|30175}} ===
tests timing out
* {{Z|30188}}
* {{Z|30913}}
* {{Z|30958}} passing
=== {{Z|30157}} ===
test timing out
* {{Z|30165}}
* {{Z|30908}} passing; trivial
=== {{Z|30217}} ===
tests timing out
* {{Z|30230}}
* {{Z|30231}}
=== {{Z|27610}} ===
test timing out
* {{Z|27611}}
=== {{Z|30471}} ===
no good tests; {{Z|30853}} would make it easier
=== {{Z|31019}} ===
tests timing out
* {{Z|31022}}
* {{Z|31023}} passing
* {{Z|31024}}
* {{Z|31025}} passing
=== {{Z|31041}} ===
test timing out
* {{Z|31043}}
* {{Z|31044}} passing
=== {{Z|10956}} ===
test timing out, connected anyway
* {{Z|10959}}
* {{Z|10961}} passing
* {{Z|19167}} passing
=== {{Z|10944}} ===
tests timing out, connected anyway
* {{Z|10946}}
* {{Z|10947}}
* {{Z|10948}}
* {{Z|10949}}
* {{Z|10950}}
* {{Z|10951}}
* {{Z|10969}}
=== {{Z|30153}} ===
test timing out
* {{Z|31708}}
=== {{Z|19243}} ===
tests timing out, connected anyway
* {{Z|19251}}
* {{Z|19395}}
* {{Z|21612}}
* {{Z|22814}}
=== {{Z|19170}} ===
tests timing out for RLE impl., connected anyway
* {{Z|19171}}
* {{Z|19172}}
* {{Z|19173}}
* {{Z|19174}}
* {{Z|19176}}
=== {{Z|34793}} ===
tests timing out, connected anyway
* {{Z|34794}}
* {{Z|34795}}
* {{Z|34796}}
* {{Z|34843}}
=== {{Z|25351}} ===
test [[Z35555]] failing for ??? reasons; tried in Echo but apply2 repeated the last elem again, but the types seemed to be good; [[Z35556]] should pass it but the others are expected to fail since they hardcode Q199
=== {{Z|35585}} ===
tests timing out
=== {{Z|35597}} ===
one impl. works modulo typing, but even when I add the typing shims it still fails the test for ??? reasons; other impl. doesn't seem to work for ??? reasons
=== {{Z|32585}} ===
product composition passes all tests; indexing composition times out
=== {{Z|28482}} ===
composition failing with <code>function call: "'int' object is not iterable"</code>, though the expression works when echo'd
=== {{Z|24139}} ===
tests timing out for composition
=== {{Z|35278}} ===
tests timing out for composition, except tiny test which has a cached failure but passes in edit mode
=== {{Z|36174}} ===
tests failing for ??? reasons; connected JS impl since I confirmed that was working
idiy99swyrvqvf3prg06m96hn0rm5wq
Z32534
0
78784
281214
281190
2026-06-07T12:38:33Z
HenkvD
1290
Simple +mk +os
281214
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z32534"
},
"Z2K2": {
"Z1K1": "Z14294",
"Z14294K1": [
"Z14293",
{
"Z1K1": "Z14293",
"Z14293K1": "Z32536",
"Z14293K2": "Z33034"
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z32591",
"Z14293K2": "Z33056"
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z32688",
"Z14293K2": "Z34003"
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z32910",
"Z14293K2": [
"Z60",
"Z1592"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z33026",
"Z14293K2": "Z33463"
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z34459",
"Z14293K2": [
"Z60",
"Z1011"
]
},
{
"Z1K1": "Z14293",
"Z14293K1": "Z36166",
"Z14293K2": [
"Z60",
"Z1157",
"Z1216",
"Z1532",
"Z1576",
"Z1137",
"Z1106",
"Z1402",
"Z1798"
]
}
],
"Z14294K2": "Z36098"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "config for creative work - entity, class, creator"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1004",
"Z11K2": "config oeuvre - entité, classe, créateur/trice"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
82yanp8onccslf4o0h84i9xvyxzaz7c
User talk:Rachmat04
3
79351
281659
280721
2026-06-08T03:08:10Z
SpBot
978
archive 1 section: 1 to [[User talk:Rachmat04/Archives/2026]] (after section [[User talk:Rachmat04/Archives/2026#Wikifunctions_&_Abstract_Wikipedia_Newsletter_#247_is_out:_References_from_Wikidata_now_available|Wikifunctions_&_Abstract_Wikipedia_Newsletter_#247_is_out:_References_from_Wikidata_now_available]]) - previous edit: [[:User:MediaWiki message delivery|MediaWiki message delivery]], 2026-06-05 14:14
281659
wikitext
text/x-wiki
{{Autoarchive resolved section
|age=3
|timeout=30
|show=yes
|archive='((FULLPAGENAME))/Archives/((year))'
|overview=
|latest archive=[[{{FULLPAGENAME}}/Archives/{{CURRENTYEAR}}|Archives/{{CURRENTYEAR}}]]
}}
== Wikifunctions & Abstract Wikipedia Newsletter #248 is out: A higher meaning ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-05-15|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we discuss functions creating language fragments, we present our latest news in Types, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 14:36, 15 May 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30536976 -->
== Wikifunctions & Abstract Wikipedia Newsletter #249 is out: Annual plan 2026-2027 ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-05-23|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we present you the current draft of objectives for Wikifunctions and Abstract Wikipedia in the WMF Annual Plan 2026-2027, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 09:48, 25 May 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30536976 -->
== Wikifunctions & Abstract Wikipedia Newsletter #250 is out: Looking back and forward ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-05-30|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we present you a recollection of our work so far, now that we celebrate our 250th newsletter, we share with you a summary of our latest outreach activities, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 10:04, 1 June 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30606821 -->
== Wikifunctions & Abstract Wikipedia Newsletter #251 is out: The illustrated encyclopaedia ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-06-05|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we introduce our first function to import images on Abstract Wikipedia, we present our Functions of the Week, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Also, we remind you that if you have questions or ideas to discuss, the next '''Volunteers' Corner''' will be held on '''[https://zonestamp.toolforge.org/1780939800 June 8, at 17:30 UTC]''' ([https://meet.google.com/xuy-njxh-rkw link to the meeting]).
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 14:14, 5 June 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30606821 -->
cmtzmx2t21babwd7dcchax3hgj0rpy3
Wikifunctions:Project chat/Archive/2026/04
4
80104
281655
280146
2026-06-08T03:08:05Z
SpBot
978
archiving 1 section from [[Wikifunctions:Project chat]] (after section [[Wikifunctions:Project chat/Archive/2026/04#Z6830_for_Chinese|Z6830_for_Chinese]])
281655
wikitext
text/x-wiki
{{Talkarchive}}
== Wikifunctions & Abstract Wikipedia Newsletter #242 is out: Request for Discussion: Syntactic tables ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-04-02|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we introduce a proposal for Natural Language Generation, we introduce a page for function suggestions from Abstract Wikipedia, we inform you that there will be a presentation about Abstract Wikipedia at WikiCon Australia, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Also, we remind you that if you have questions or ideas to discuss, the next '''Volunteers' Corner''' will be held on '''[https://zonestamp.toolforge.org/1776101400 April 13, at 17:30 UTC]''' ([https://meet.google.com/xuy-njxh-rkw link to the meeting]).
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:37, 3 April 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
:<small>This section was archived on a request by: [[User:Mdktb|Mdktb]] ([[User talk:Mdktb|talk]]) 17:13, 10 April 2026 (UTC)</small>
== Special:CreateObject doesn't let me create a typed list ==
Hello. I tried to create a typed list with all English varieties on Wikifunctions, to create consistency between different configs, but [[Special:CreateObject]] doesn't show typed list as an option for creating. Is this a bug or an intentional restriction? [[User:ChaoticVermillion|ChaoticVermillion]] ([[User talk:ChaoticVermillion|talk]]) 08:24, 2 April 2026 (UTC)
:@[[User:ChaoticVermillion|ChaoticVermillion]]: A typed list is an instance of a function call, which is prohibited as a persisted Object. Maybe you want an enum? See [[Wikifunctions:Type proposals]] for the process for creating such a thing. [[User:Jdforrester (WMF)|Jdforrester (WMF)]] ([[User talk:Jdforrester (WMF)|talk]]) 12:01, 2 April 2026 (UTC)
::I don't want any sort of enum for this, I just want a list of English varieties (English, American English, Australian English, etc.) as an object that I can put in configs. I tried making an object as a function call, but apparently I have to be a member of the staff to do it. [[User:ChaoticVermillion|ChaoticVermillion]] ([[User talk:ChaoticVermillion|talk]]) 12:07, 2 April 2026 (UTC)
:::I’ve started this with {{Z|Z33034}}. I’m guessing you were trying to create an object of type {{Z|Z7}} rather than an object whose type is a [[Z7]]. There’s a subtle but important difference. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 12:29, 2 April 2026 (UTC)
::::How do you create a object whose type is a [[Z7]] then? I can't see any option to do this. [[User:ChaoticVermillion|ChaoticVermillion]] ([[User talk:ChaoticVermillion|talk]]) 12:32, 2 April 2026 (UTC)
:::::When you are creating an object you must specify its type. By default, the UI expects you to provide a reference to the type, so you can just type in the selector box and click the type you want. For an object like a typed list, the type is specified as a function call, so you click the menu dots beside “type” and choose “function call” instead of “reference”. The selector now allows you to search for functions that can return a type object. You start entering “typed list” and select it in the normal way. Then the UI will allow you to specify the argument to the function call, which is the type shared by all elements in the list. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 13:12, 2 April 2026 (UTC)
== Unable to use the Z32643 function. ==
I'm trying to use the [[Z32643]] (Article-less multi instantiating fragment) function to edit an article in Abstract Wikipedia, but it looks like it's missing connected implementations, and it looks like i don't have the permissions to connect the function with the implementation and use it in ab.wiki.
Since i'm not familiar with Wikifunctions, i would like to get help for this specific problem.
Thanks. --[[User:Mattiz6276|Mattiz6276]] ([[User talk:Mattiz6276|talk]]) 13:50, 2 April 2026 (UTC)
:@[[User:Mattiz6276|Mattiz6276]]: Hey there, it looks like that's a work-in-progress by @[[User:MetalBreaksAndBends|MetalBreaksAndBends]]. There's only a fall-back implementation, Z32652, which fails its own test for a second item. I can connect the items together but it might not work as you expect? [[User:Jdforrester (WMF)|Jdforrester (WMF)]] ([[User talk:Jdforrester (WMF)|talk]]) 14:06, 2 April 2026 (UTC)
::Maybe… I don’t think functions without implementations should be appearing on AW so I have amended the label to avoid giving the impression that this function is available. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 14:23, 2 April 2026 (UTC)
== Being able to use edit summaries ==
I have wanted to use edit summaries a few times when connecting or disconnecting implementations or tests, and I have found that the only way to do that is to use the [[:User:%D7%9E%D7%A7%D7%A3/wikilambda_editsource.js|wikilambda editsource]] tool. I feel like it would be a lot more convenient to be able to make these edit summaries without having to use that tool. [[User:ChaoticVermillion|ChaoticVermillion]] ([[User talk:ChaoticVermillion|talk]]) 08:40, 3 April 2026 (UTC)
== Definite forms ==
[[Talk:Z32162|I've noticed an incomplete implementation and would like to work on fixing it, but need advice.]] [[User:Ijon|Ijon]] ([[User talk:Ijon|talk]]) 21:05, 3 April 2026 (UTC)
== Wikifunctions & Abstract Wikipedia Newsletter #243 is out: Community proposals for capturing meaning ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-04-10|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we report on three community proposals on syntactic approaches, we introduce a new Type (Complex numbers), we report on current hiccups on Abstract Wikipedia, we share more information about a presentation about Abstract Wikipedia at WikiCon Australia, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Also, we remind you that if you have questions or ideas to discuss, the next '''Volunteers' Corner''' will be held on '''[https://zonestamp.toolforge.org/1776101400 April 13, at 17:30 UTC]''' ([https://meet.google.com/xuy-njxh-rkw link to the meeting]).
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 15:35, 10 April 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
:<small>This section was archived on a request by: [[User:Sannita (WMF)|Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:12, 3 May 2026 (UTC)</small>
== Wikifunctions & Abstract Wikipedia Newsletter #244 is out: Milestones; Some major issues hopefully resolved ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-04-16|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we celebrate 4000 functions on Wikifunctions and 1000 abstract articles on Abstract Wikipedia, we announce that we should have fixed some major issues with the websites, we inform you on our latest outreach activities, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 10:22, 17 April 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
:<small>This section was archived on a request by: [[User:Sannita (WMF)|Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:12, 3 May 2026 (UTC)</small>
== Help me understand why test passes but running function with same input errors ==
I've been working on a function that I thought I had working correctly: {{Z+|Z31108}}.
It has a test which is passing, but when I enter the same Wikidata item as the test uses (or any other relevant item that it ought to work for), it errors. I can't figure out what's wrong (and the function itself seemed to be working as expected the last time worked on it, a month ago), and I don't know how to interpret the error info.
The error output is:
<code>
Error type: Unspecified error
Error data:
error information: "cannot read property 'Z6003K4' of undefined"
Execution debug logs:
Z12696K2 (find this) ==> ZObject<Z1K1:{'Z1K1': 'Z9', 'Z9K1': 'Z6007'},Z6007K1:ZObject<Z1K1:{'Z1K1': 'Z9', 'Z9K1': 'Z6092'},Z6092K1:P3831>,Z6007K2:ZObject<Z1K1:{'Z1K1': 'Z9', 'Z9K1': 'Z6091'},Z6091K1:Q7380503>,Z6007K3:ZReference<Z6021>>
</code>
-- [[User:Ragesoss|Ragesoss]] ([[User talk:Ragesoss|talk]]) 18:02, 10 April 2026 (UTC)
:Hmm… I’ve had a quick look and found a comparable error with {{Z|Z29937}}, which was passing “4 months ago”. The debug comes from {{Z|Z12863}}, after it fails to find a match. This is probably because Z6007K3 is ZReference("Z6021") rather than a Z6020 object, as it appears in the debug logs for your test “28 days ago”:
:"Z6007K3":{"Z1K1":{"Z1K1":"Z9","Z9K1":"Z6020"},"Z6020K1":{"Z9K1":"Z6021"}}
:This means it’s probably a bug introduced by a recent software change. I’ll take a closer look over the weekend, but the actual error for your test case now is:
:Unspecified error (error information: "cannot read property 'Z6003K4' of undefined")
:I’m guessing that’s from there being no guard against an empty array in {{Z|Z23681}}, but we’ll see. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 23:22, 10 April 2026 (UTC)
:I tracked down the bug to [[Z29869]] and fixed that, but the tests for [[Z31659]] are still failing. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 05:44, 11 April 2026 (UTC)
::Thanks. I’ve re-implemented {{Z|Z23680}} with {{Z|Z33331}} so we at least get a meaningful error in {{Z|Z31108}}. The new implementation explicitly caters for the alternative representation of {{Z|Z6040}} and errors on the empty list it gets from {{Z|Z31659}}.
::I also made a [[Z33332|simple Python implementation]], which seems to work. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 16:29, 11 April 2026 (UTC)
== No place to ask to activate a function? ==
I just discovered the {{Z|Z11390}} function and decided to make an overarching language-selecting function for it. After some fiddling around and studying how {{Z|Z32321}} and its set works, I now have {{Z|Z33439}} which has a config object ({{Z|Z33441}}) that selects between the two preexisting language-specific versions I found (English and Malayalam) and the new one I created for my other native language, {{Z|Z33440}}.
Except I can't get these to work, because, from what I understand, only [[Wikifunctions:Functioneers|functioneers]] can activate newly created functions and implementations?
So where do I apply for the {{Z|Z33439}} and {{Z|Z33440}} functions to be activated? Perhaps we should have a place to ask for it, a la [[w:WP:NPP]] — call it [[Project:New Functions Patrol|New Functions Patrol]] if you will. —[[User:UndueMarmot|<span style="color:#705ccb;">Undue</span><span style="color:#cfdfa3;background-color:#705ccb;">Marmot</span>]] ([[User talk:UndueMarmot|talk]]) 08:58, 14 April 2026 (UTC)
:There's [[Wikifunctions:Community portal]] for this. By the way, {{Z|Z33445}} doesn't work because all the code must be inside the main function in Python code. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 09:19, 14 April 2026 (UTC)
::Huh. How could I have missed that?
::But anyway, I've moved the code in {{Z|Z33445}} into inside the main function block.
::Is there any reason why WF decided not to simply allow anyone to write functions? After all, that's how it works with wikitext templates, and these functions are fundamentally serving the same purposes as templates, just written in a different way. —[[User:UndueMarmot|<span style="color:#705ccb;">Undue</span><span style="color:#cfdfa3;background-color:#705ccb;">Marmot</span>]] <small>([[User talk:UndueMarmot|talk]])</small> 11:25, 14 April 2026 (UTC)
:::I've fixed a couple of technical issues, but tests still fail. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 11:28, 14 April 2026 (UTC)
::::The tests pass now after some more changes to the code: [[Special:Diff/265232/cur]]. —[[User:UndueMarmot|<span style="color:#705ccb;">Undue</span><span style="color:#cfdfa3;background-color:#705ccb;">Marmot</span>]] <small>([[User talk:UndueMarmot|talk]])</small> 12:01, 14 April 2026 (UTC)
:::::{{D|Connected}} [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 12:02, 14 April 2026 (UTC)
:::I think it's a security issue, since Wikifunctions implementations could teoretically be malicious. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 11:29, 14 April 2026 (UTC)
::::This ^^^. We're very lenient, though, once you've created a few working functions you can apply for the right, per [[WF:Functioneer]]. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 12:26, 14 April 2026 (UTC)
::::That explanation [[phab:T343559#11421293|doesn't add up]]. A reason to limit ''connection powers'' to Functioneers might be to prevent vandalism, considering Functions can be used across wikis. But user-provided code is already being executed even without a Functioneer connecting it. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 20:01, 16 April 2026 (UTC)
== Actual difference between {{Z|Z26039}} and {{Z|Z26095}} ==
What is the actual difference between these two functions? I ask, since it seems to me that the current distinction is more or less that the first one doesn't use an indefinite article in English, while the second does. Which is not a good distinction in a project that should be language neutral. This doubt emerged from my use of the first one in [[abstract:Q124441]], which @[[User:Hogü-456|Hogü-456]] made me notice that is probably wrong. My question is: why is it wrong? How could we clarify the difference? [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 19:46, 19 April 2026 (UTC)
:I think the difference is if there is an indefinite article like a or an before the subject or not. In German there can be cases where a definite article is necessary before the subject. I looked at the functions and before the object both times an article is mentioned. As it depends on the language and the word what is the correct function to use I hope it will be clarified and it is an example of the necessity to have a human with understanding in a specific language check it. I hope there will be longer functions what generate more content about a specific kind of item. Then it is necessary to write one such function per language and it can be then applied to several items. It still requires checks and so maybe it is better to write down what item category needs what kind of introduction sentence function for what language. [[User:Hogü-456|Hogü-456]] ([[User talk:Hogü-456|talk]]) 20:05, 19 April 2026 (UTC)
::The point of these two functions (and of the entire Abstract Wikipedia project) is that they should be defined in a purely language-independent way, so that the translation to actual language can be done automatically. This is the reason why these functions have been renamed; I think that this attempt was not succesful, since meaning is still unclear. My proposal to clarify them would be to invoke the concept of [[w:specificity (linguistics)|specificity]]: {{Z|Z26039}} should be named "specific subject is instance of (string)", and should be used only when the QID of the subject uniquely identifies a single thing; {{Z|Z26095}} should instead be renamed in "non-specific subject is kind of (Monolingual text)", and should be used when the QID refers to a multitude of real life items, and we are specifying the class that all these item belong to. This clarification would not still be enough, since it doesn't explain how mass nouns are handled (is water a unique thing? Does {{Q|Q7802}} refer to a single piece of bread or to the entirety of bread, like water?). This problem is very tricky, since mass noun are language-specific and blurry the line between these two functions. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 20:58, 19 April 2026 (UTC)
:Simply put, one corresponds to P31 and the other to P279. Paris is an {{Q|P31}} a {{Q|Q1549591}}
:whereas a {{Q|Q1549591}} is a {{Q|P279}} (alias “kind of”) {{Q|Q515}}. Whether the Wikidata knowledge representation will be sufficient to resolve into fluent natural language representations in all languages is, of course, a crucial question. Where it is not, the Abstract Wikipedia knowledge representation will need to supplement the Wikidata content with additional details about the relation between the participants or the participants themselves, and these details should be language-neutral, to the extent that this is practicable. The item {{Q|Q124441}} has no [[:d:Q124441#P31|P31]] statements; it has only [[:d:Q124441#P279|P279]]s, including one relating it to {{Q|Q212920}}, which suggests {{Z|Z26095}} is the appropriate choice here even if the rendering in some languages is the same. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 22:52, 19 April 2026 (UTC)
::Thanks for explaining it. I think that I'll change the implementation of {{Z|Z26095}}, so that in Italian it produces more or less the same output of {{Z|Z26039}} (both with the definite article). [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 11:24, 20 April 2026 (UTC)
== Equivalent of Z6830 for lexemes ==
Is there an equivalent of {{Z|Z6830}} that enables retrieving all lexemes pointing to a particular lexeme using a specific property? [[User:Redmin|Redmin]] ([[User talk:Redmin|talk]]) 21:06, 20 April 2026 (UTC)
:There's {{Z|6831}} but I think that's slightly different again to what you're after. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 12:31, 21 April 2026 (UTC)
::Thanks for sharing that one, I did not know it existed. But you are right, it’s not quite what I am looking for. I want a function that would take a Wikidata property reference (like P5191, which is ‘derived from lexeme’) and a Wikidata lexeme reference, and return a list of lexemes that reference that lexeme using that property. [[User:Redmin|Redmin]] ([[User talk:Redmin|talk]]) 13:23, 21 April 2026 (UTC)
== Storing huge datasets ==
It is not a necessity I suppose, but an idea came to me earlier to write a function that would give the corresponding [[en:Shavian alphabet|Shavian alphabet representation]] of an English word written with the Latin alphabet, or perhaps apply that operation to an entire sentence. However, just trying to guess as to what the IPA pronunciation of each word passed into it could be is is both not ideal (pronunciations obviously can and will vary widely between accents) and infeasible <s>(Wikidata lexemes don't really seem to account for pronunciation)</s>. It happens, though, that a comprehensive Shavian dictionary exists named the [https://readlex.pythonanywhere.com/ ''Read Lexicon''], which uses pronunciation and spelling similar to that used by the creator of the alphabet himself. This would be a good dataset to use in performing this translation in the function, but it appears that, all in all, the total size of the dictionary is [https://github.com/Shavian-info/readlex/blob/main/readlex.json ''nearly 26MB''] when formatted as JSON, which would certainly be larger when converted into a typed list.
I am wondering if this will ever be feasible or admissible, or if there is really a way around this if importing such a large set of data is deemed impractical. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 22:30, 20 April 2026 (UTC)
:{{re|Theki}} "Wikidata lexemes don't really seem to account for pronunciation"" is patently untrue; not only does every Bokmål lexeme form have IPA attached to it (thanks to Jon Harald Søby), but there are lots of languages--including English--that have pronunciation information, whether through IPA or otherwise, indicated on their forms. The big issue of course is that adding this data is not always possible to perform efficiently--for instance, I'd love to have Yiddish pronunciation respellings from Paul Abelson's dictionary on as many English forms as possible, but this dictionary not being previously processed makes this difficult. The data set you have brought up, if a suitable reading of [[:m:Wikilegal/Lexicographical_Data]] allows it, could be added as {{P|7243}} statements on various English forms. [[User:Mahir256|Mahir256]] ([[User talk:Mahir256|talk]]) 23:20, 20 April 2026 (UTC)
::Well, sorry... I haven't witnessed these pronunciation statements before, I wasn't aware of their existence until you pointed it out. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 15:02, 21 April 2026 (UTC)
:I got 1,900,000 characters into [[Z33875]] before the UI gave up on me. I'm not sure what the limit is. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 15:21, 21 April 2026 (UTC)
== Could not serialize input JS object: Number <small><small>[insert tested number here]</small></small> ==
I'm not one to throw my problems at others, but I have no idea how to fix this. Am implementing {{Z|Z24602}} in JavaScript, which requires returning a typed map. It now works for every type of value except numbers. Tried explicitly converting the numbers to float64, but either way it throws the error above. Would appreciate it if anyone could diagnose or fix the problem, as my knowledge of Wikifunctions is amateur at best. Thank you. [[User:Some helpful person|Some helpful person]] ([[User talk:Some helpful person|talk]]) 00:32, 23 April 2026 (UTC)
:The quick answer is that like some list-related functions, code implementations returning typed maps are not possible unless the type of the objects in the map is specified in the function signature (e.g. if it was a map from Strings to Natural numbers only). So unfortunately, I think you've chosen a function that is not really possible at the moment. There are a few ideas of how we might address this, but for the moment, work on something else. Sorry! --[[User:99of9|99of9]] ([[User talk:99of9|talk]]) 13:21, 23 April 2026 (UTC)
:Maybe explicitly using [[Z13518|natural numbers]] would work? I would try using <code>{ "Z1K1": "Z13518", "Z13518K1": "[number]" }</code> to represent numbers, perhaps, and seeing if that works. Of course, you would also probably have to adapt this for other types that cannot be serialized, and I'm not sure how easy that would be to generalize (assuming [[w:Don't repeat yourself|DRYness]] is desired). — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 14:39, 23 April 2026 (UTC)
== Please disconnect implementation ==
I think I've fixed my issue with {{Z|Z33986}}, but I can't edit an actively connected implementations with my rights. I must admit it is an AI-aided fix, I feel very strongly about disclosing that.
Courtesy pinging [[User:Theki]] and [[User:Feeglgeef]]. [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 18:51, 23 April 2026 (UTC)
: Additionally, I think the JS might be working. [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 18:52, 23 April 2026 (UTC)
: Just to clarify, I mean disconnect the Python implementation please. [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 18:53, 23 April 2026 (UTC)
:{{Done}} I've disconnected the Python implementation.
:I've also added a couple of tests. The rule is a bit more complicated than adding a maqaf before every character that is not a Hebrew letter. Unfortunately, I don't think I'll have time to fix the implementations any time soon. [[User:Amire80|Amir E. Aharoni]] ([[User talk:Amire80|talk]]) 19:03, 23 April 2026 (UTC)
:: Thank you!
:: Also, for some reason I thought you put a maqaf before all gershayim, so thanks for correcting me. [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 19:09, 23 April 2026 (UTC)
:::No, that's not the rule.
:::The rest of this reply is an [[:wikt:info-dump|infodump]], feel free to ignore it :)
:::In [https://hebrew-academy.org.il/topic/hahlatot/punctuation/ the Academy's punctuation rules], the rule for adding a maqaf is written kind of badly: שמים מקף ברצף שיש בו שני סוגי גופנים, כגון אותיות ומספרים ("maqaf is added in a sequence in which there are two types of fonts, such as letters and numerals"). These are not different types of "fonts", but different types of characters, and I should email them about it. It gives the examples <span lang="he" dir="rtl">ה־12</span> and <span lang="he" dir="rtl">ב־DNA</span>. It doesn't say anything explicitly about quotation marks, but in other places on the same page, you have stuff like <span lang="he" dir="rtl">ב"הארץ"</span>, and from that I deduce that a maqaf is not needed before double quotes if there are Hebrew letters inside the double quotes.
:::That said, a few people do think that there must be a maqaf before double quotes. I have a somewhat surprising example of somebody who always does it: translators of Scientology materials into Hebrew. At least that's what they did last time I looked at them, about ten years ago. Those people are certainly prolific, and they get points from me for consistency, but this not the prevalent standard. (And if I recall correctly, they use the minus and not the proper Hebrew maqaf, and they don't get any points from me for that!)
:::Also, the name of the character is just "double quotes" and not "gershayim". Gershayim are mostly for abbreviations, although most people use the same character for them. I use ״ for gershayim, as do a few other nerds, but we're the minority. [[User:Amire80|Amir E. Aharoni]] ([[User talk:Amire80|talk]]) 19:35, 23 April 2026 (UTC)
:::: {{re|Amire80}} Yeah, "font" is a weird choice of wording here by the Academy...<br> I think I'll follow your guidance and not use a maqaf for quotes beginning with Hebrew letters. <br> I should also add more tests for different types of quotes, like straight (", '), curly (“, ”, ‘, ’), gershayim (״, ׳), including single quotes.<br> P.S. gotta deduct points from Scientology for being a cult but that's neither here nor there [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 20:49, 23 April 2026 (UTC)
:::: {{re|Amire80}} Courtesy ping because I mistyped your username on the last message. Anyways I'm also gonna do that tomorrow because I'm tired now... [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 20:50, 23 April 2026 (UTC)
== Help with creating a function for Abstract Wikipedia ==
Hello! I was inspired by {{Z|Z26570}} to create {{Z|Z33975}}, however I'm not sure how I add specific language implementations here. Can anybody help me? [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 10:41, 23 April 2026 (UTC)
: I think I figured it out, I created a new object with the language config type, added {{Z|Z14310}} to my implementation, and added a new function for English... At least I think that's how it works... [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 13:16, 23 April 2026 (UTC)
::You have the right idea, as far as I know. I went ahead and connected the implementations you created as they appear to work fine for English, and added a test for {{Z|Z33975}} (which passes [[File:Twemoji 1f601.svg|24px]]). I also corrected an error you made on the config object where you appear to have accidentally connected English to {{Z|Z33975}} instead of {{Z|Z33977}}. Thank you for contributing! — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 13:45, 23 April 2026 (UTC)
::: {{re|Theki}} Thank you so much for you help! Could you please kindly also connect the implementations for {{Z|Z33986}} which I just made, which is going to be used for the Hebrew implementation of {{Z|Z33975}}. [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 14:11, 23 April 2026 (UTC)
::::You seem to be returning the wrong type in both implementations. Functioneers should not connect implementations that don't work for non-functioneers. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 14:13, 23 April 2026 (UTC)
::::: {{re|Feeglgeef}} Oh thank you for pointing that out! I am still a bit new to this project and confused, so I need to read up some more about this. How do I return a monolingual text object? [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 14:17, 23 April 2026 (UTC)
::::::I'm trying to fix it for you, the construction of ZObjects in code implementations is a bit difficult right now. Since the State origin using entity and class function will (presumably) be composition, perhaps {{Z|33975}} can be adjusted to return a string, using {{Z|26107}} and {{Z|26107}}? [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 14:21, 23 April 2026 (UTC)
:::::I did not notice any discrepancies from looking at the functions by themselves, and it seemed to work fine on my end. Is it bad practice for NLG functions to return the monolingual text type? I had assumed it was logical. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 14:24, 23 April 2026 (UTC)
::::::Both implementations are failing all three tests on my end. No consensus has been established as to whether monolingual texts or strings should be used, so it's like the [[w:War of the currents|war of the currents]] but for Wikifunctions. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 14:27, 23 April 2026 (UTC)
:::::::Oh, you were referring to {{Z|Z33986}}. I assumed you were stating that something was wrong in the earlier English functions that I missed; I apologize for the misunderstanding. Has there been any centralized discussion on this string vs. monolingual text issue? — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 14:31, 23 April 2026 (UTC)
::::::::Not that I'm aware of, I've brought it up on the telegram twice before, though. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 14:49, 23 April 2026 (UTC)
::::::::The centralised discussion is at [[WT:Abstract Wikipedia/2025 fragment experiments#Proposed recommendation: Fragments should return Z11/monolingual strings]]. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 07:19, 24 April 2026 (UTC)
::::::: {{re|Theki|Feeglgeef}} Can only functioneers test implementations? For me I can't test it at all... [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 14:32, 23 April 2026 (UTC)
::::::::AFAIK, test cases are only immediately testable during editing of a function if they are connected. This is one of my personal pain points with Wikifunctions, iterating on functions without exhaustive connected test cases makes debugging practically impossible for non-functioneers working on newly-created functions... I (or Feeglgeef) can quickly connect the tests you need for you if you want, although if they are not well-formed they may need to be disconnected again afterwards. Additionally, I could temporarily connect the implementation you are writing so that you can test it on the sidebar as you work, but I'm not sure if this is advisable. That functionality is also something that unfortunately only works when not disconnected. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 14:43, 23 April 2026 (UTC)
:::::::::Yup, agree with you on all points, thanks. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 14:48, 23 April 2026 (UTC)
== Connect implementations ==
Hello!
I'm done with the implementations of {{Z|Z33986}} both in JS and Python, and all tests pass.
Pinging @[[User:Amire80|Amire80]] to check if all the tests I've added are alright. [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 15:24, 24 April 2026 (UTC)
:Connected. It's possible that some more changes will be needed, but it looks OK now.
:Another little comment: It should be called "clitic" and not "prefix". [[User:Amire80|Amir E. Aharoni]] ([[User talk:Amire80|talk]]) 15:56, 24 April 2026 (UTC)
: {{re|Amire80}} Thanks for your comment! Luckily labels are easy to edit, so I'll get to it.
: Currently I'm working on Bulgarian {{z|Z34072}} and {{z|Z34084}}, along with other Bulgarian functions. After I'm finished with those I'll take your advice. [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 16:01, 24 April 2026 (UTC)
== Please connect my Bulgarian implementations ==
I recently created the following Bulgarian functions:
* {{Z|Z34070}} (currently broken, I think because another function I built it upon is unimplemented)
* {{Z|Z34088}}
* {{Z|Z34105}}
* {{Z|Z34072}}
* {{Z|Z34084}}
Can somebody please connect these functions, and perhaps suggest other functions I could localize? [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 19:03, 24 April 2026 (UTC)
: Specifically, [[Z34070]] is based on [[Z34072]] [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 19:05, 24 April 2026 (UTC)
:{{done}} for everything that passes, [[Z34070]] still does not work after purging WF's cache, though. For future reference, please request on the [[WF:Community portal|community portal]] instead of the project chat. Thank you for your work! [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 20:03, 24 April 2026 (UTC)
:: {{re|Feeglgeef}} Thank you for your help! I will keep in mind to go to the [[project: community portal| community portal]] in future instead for this.
:: I still don't understand why {{Z|Z34070}} fails... It's implementation is almost completely identical to [[Z30399]] from {{Z|Z30397}}, unless I messed something up... [[User:QuickQuokka|QuickQuokka]] ([[User talk:QuickQuokka|talk]]) 20:11, 24 April 2026 (UTC)
:::{{done}} No, it was mostly just timing out. It is better to use selective fetches where possible. One case is failing to match the expected results, but at least it is returning something. For all I know, it might even be acceptable! [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 20:35, 24 April 2026 (UTC)
:::: {{re|GrounderUK}} Thank you so much for your help! The one failed case is with a definite article, so I feel like that might be fixed in the future... <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 20:43, 24 April 2026 (UTC)
== Editor experience suggestions ==
I'm a bit frustrated with the editing experience on Wikifunctions, and I have suggestions based on pain points I've had contributing to this project:
* Adding a wizard to create functions, implementations, and tests in one flow, somewhat like Wikimedia Commons' upload wizard
* A sandbox for experimenting without changing mainspace functions, and maybe letting non-functioneers connect implementations ([[Project: Sandbox]] doesn't seem to fit this)
* We could have functions for creation based on the sandbox, like how Wikipedia has articles for creation and edit requests,
* Maybe even another test instance of Wikifunctions, like how Wikidata has [[testwikidata:|Test Wikidata]]
I really like this project and I don't mean to whine, but it certainly has a lot of pain points both for technical and non-technical people. <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 20:41, 24 April 2026 (UTC)
: It's also really complicated to localize functions, so maybe we should add another wizard for that, where you can choose a language, and then create the new function with the aforementioned function wizard, and it just automatically adds it to the related language configuration object of the related function. <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 20:48, 24 April 2026 (UTC)
:# Sounds good to me.
:# A sandbox available is [[Z10119]], though an extension-provided sandbox that allows you to manipulate the types, code, and tests easily without interfering with the mainspace would be nice.
:# [[WF:Suggest a function|This page]] works to some extent, though it's too messy in my opinion.
:# We used to have a "beta cluster" but it [[phab:rOMWC5f625c5846b5f779473fa32c9a02d87e59215dfa|got shut down]] just over a year ago because it was broken.
:[[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 21:07, 24 April 2026 (UTC)
== Language parameters in language-specific functions ==
I think that an effort should be made to give the different natural language options corresponding to different English dialects, Chinese scripts, etc. more of a use
(I added the test {{Z|Z34119}} to {{Z|Z26095}} and unsurprisingly it fails). There are two main problems with this approach that I can identify:
* If you ask the majority of these functions to make a sentence in British English, much of the time it will give you an output with missing words, because it does not fallback to English labels in the case of a British English label for that item missing. The same applies for every other English dialect, British English is just an example here.
* Uninformed editors will probably see the presence of a language parameter on these functions, consider it redundant, and remove it. [[Special:Diff/268074|I have made this mistake before]].
In my opinion, in a perfect world, all of these language generation functions would output monolingual text, and if the user asks for American English text, then American English text is what they'll get. If the user asks for Japanese text in hiragana specifically, then that's what they'll get in return. This is not as high-priority as just rendering text in the language plainly in the first place, but it's something that I feel is still worth devoting some effort to.
Right now switching functions to use this paradigm is difficult because, on the one hand, I don't know if consensus tends towards this direction being ideal or advisable, and I don't want to make changes like this without at least notifying the wider community. In addition, all tests break once a parameter is added or removed, and the function editor does not recognize the change in number of parameters and therefore you have to remove the function call, re-add it along with all of its parameters it had previously (which is a tedious cut-and-paste job), and then it will work again. This is something that you can do in five seconds by just adding a few lines of JSON to the test source, but this is not directly editable from the Web browser. This tedium is largely what's preventing me from doing this on a larger scale, besides asking for comments first.
If anyone has any insights or comments on this, then that would be appreciated. If a reference of functions with and without the support for language variants is needed, of course [[WF:NLG]] can be perused, but I've also my own list cataloged at [[User:Theki/functions#language]]...
Of course, this thread has many similarities to [[#"language" argument for certain functions|the one above]], but this concerns me going out and making this consistent across these NLG functions. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 21:09, 24 April 2026 (UTC)
:I've created {{Z|Z34122}} as an extension to {{Z|Z34039}} for larger functions. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 21:34, 24 April 2026 (UTC)
:Just to confirm that I, for one, support a Natural language parameter for all natural-language functions. The concern about getting them all aligned is just that we haven’t finally settled on {{Z|Z11}} being preferred to {{Z|Z89}} or some other type that conserves the text’s provenance, so we risk having to change them all again. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 10:07, 25 April 2026 (UTC)
== Why is my test failing? ==
Hello! I recently made {{Z|Z34139}} based on [[wikt:Module:bg-translit]], and the test case {{Z|Z34141}} is failing on both implementations, despite the expected output and actual output being the same as far as I can tell.
I tried looking at the Unicode codepoints of the output, but those are also identical. <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 06:21, 25 April 2026 (UTC)
:Yes, it’s a tricky one. I’ve added a normalize step to the result validation in {{Z|Z34141}}, which confirms it is a normalization issue. It looks like it is in the code but I don’t know whether simply normalizing the result is the way to go. Logically, you would normalize both the input and the result. The implementations of {{Z|Z10384}} show you how to do this. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 09:41, 25 April 2026 (UTC)
== Edit request ==
Hello! I have an edit request for {{Z|Z23752}} and {{Z|Z23414}}.
Please replace all the "an/a" logic with <code>Z21739(Argument reference)</code>, both for readability and for more accuracy ("a university is an institution") <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 09:50, 25 April 2026 (UTC)
:{{D}} [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 12:24, 25 April 2026 (UTC)
== Wikifunctions & Abstract Wikipedia Newsletter #245 is out: The Foundation's search for the perfect language ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-04-25|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we present an academic paper about Abstract Wikipedia, we discuss our latest Type created, and we take a look at the newest created functions.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 09:54, 25 April 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
:@[[User:Sannita (WMF)|Sannita (WMF)]], @[[User:DVrandecic (WMF)|DVrandecic (WMF)]], technical meta-question: the newsletter quotes the article:
::the only way to contest its algorithm is to click 👍 or 👎 (Crawford and Gillespie 2016)
:This quotation sounds sensible, but the article's [https://link.springer.com/article/10.1007/s00146-026-02899-w web version], and the PDF that is downloadable from the same page doesn't actually show the emojis. It rather shows text that looks lacking:
::the only way to contest its algorithm is to click or (Crawford and Gillespie [https://link.springer.com/article/10.1007/s00146-026-02899-w#ref-CR14 2016])
:Where did you get the emojis? Is it your (probably correct) guess or is there a version somewhere that actually shows the emojis? [[User:Amire80|Amir E. Aharoni]] ([[User talk:Amire80|talk]]) 15:16, 25 April 2026 (UTC)
::Scratch that. I've found a version with correct emojis: https://wikihistories.github.io/wikilambda-the-ultimate/ [[User:Amire80|Amir E. Aharoni]] ([[User talk:Amire80|talk]]) 15:37, 25 April 2026 (UTC)
== Requested deletion of test ==
Please delete {{Z|Z34143}}. this was never valid Bulgarian, I messed up. <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 10:19, 25 April 2026 (UTC)
:[[WF:RFD]] please. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 16:40, 25 April 2026 (UTC)
== Request for comment (global AI policy) ==
<bdi lang="en" dir="ltr" class="mw-content-ltr">A [[:m:Requests for comment/Artificial intelligence policy|request for comment]] is currently being held to decide on a global AI policy. {{int:Feedback-thanks-title}}
[[User:MediaWiki message delivery|MediaWiki message delivery]] ([[User talk:MediaWiki message delivery|talk]]) 00:57, 26 April 2026 (UTC)</bdi>
<!-- Message sent by User:Codename Noreste@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Distribution_list/Global_message_delivery&oldid=30424282 -->
== “Key not found ()”? ==
What am I doing wrong in {{Z|Z34137}}? [[User:Redmin|Redmin]] ([[User talk:Redmin|talk]]) 00:39, 25 April 2026 (UTC)
:You were passing a [[Z6091]] to {{Z|32290}}, but it takes a [[Z6001]]. Fixed. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 07:10, 26 April 2026 (UTC)
::{{done|Thank you}}! [[User:Redmin|Redmin]] ([[User talk:Redmin|talk]]) 14:13, 26 April 2026 (UTC)
== Is it OK to connect the implementation? ==
Hello!
I recently applied for functioneer on [[WF:RFG]], and I was wondering whether I could connect the implementation for {{Z|Z34165}} despite its dependency {{Z|Z34149}} being currently unimplemented. That is <em>if</em> I get accepted.
I am planning on implementing it based on [[wikt:module:bg-nominal]], but am still having trouble figuring it out for now. <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 09:04, 26 April 2026 (UTC)
== Past tense function ==
Is there a function like {{Z|Z26039}}, but for the past tense (e.g. "Leo Tolstoy <em>was</em> a writer.")?
If not, I will create it myself, I just want to make sure there's not a duplicate. <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 10:10, 26 April 2026 (UTC)
: {{Done}} with {{Z|Z34224}}, but I have a few kinks to work out with it. <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 10:35, 26 April 2026 (UTC)
:: I need to create some other similar functions for the past tense, I have some ideas:
::* {{Z|Z26095}}
::* {{Z|Z32643}}
::* {{Z|Z28016}}
::* {{Z|Z26570}}
::* {{Z|Z33975}}
::* {{Z|Z27243}}
::* <ins>{{Z|Z26627}}</ins>
::* <ins>{{Z|Z27627}}</ins>
::* <ins>{{Z|Z27173}}</ins>
::* <ins>{{Z|Z29591}}</ins>
::
:: Are there any I have missed? <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 10:57, 26 April 2026 (UTC)
:I think that here we are starting to walk on dangerous waters: what does past mean? Is it a recent o a far past? Does it have ripercussions on the present or not? Is it just a thing that happened once, many times or for a continuative period of time?
:Consider that various languages distinguish between many different types of past. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 12:38, 26 April 2026 (UTC)
:: {{re|Dv103}} That is a fair point...
:: How do we go about solving this problem though? I don't think having every sentence on Abstract Wikipedia be "X is a Y" is a very good idea.
:: Maybe we have different functions for all these variations of past you mentioned that just map into "X was a Y." in English? <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 13:31, 26 April 2026 (UTC)
:::To properly solve this problem, we should use a more complete abstract content representation model, like for example the proposal of [[Wikifunctions:Type proposals/Semantic unit|Semantic units]] (look at [[Wikifunctions:Type proposals/Semantic unit/Douglas Adams|the example]] to see how times could be handled). For now, since we're still stuck with single fragment generation functions (that I hope will be slowly replaced with the complete represenation model, when available), we could just restrict your function to a very specific meaning, like "subject was an instance of, for most of its existence" (which means for example that it could be used to say "Douglas Adams was a writer", but not "Abraham Lincoln was a president", since he only was a president for 4 years). Probably my definition is still too vague, and this is why we need to go beyond these fragment generating functions. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 19:47, 26 April 2026 (UTC)
::Nitpick... I don't like that it outputs a string instead of monolingual text. With {{Z|Z26039}} it's used so much that I think it's unfixable in that case beyond deprecating it if people care that much, but {{Z|Z34224}} doesn't even have any connected implementations yet. Consider it, maybe?
::Nitpick 2... {{Z|Z34227}} is missing a language parameter. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 16:08, 26 April 2026 (UTC)
::: I will consider that!
::: I just did that because that's what {{Z|Z26039}} does, so I assumed I should follow suit with it. <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 16:48, 26 April 2026 (UTC)
== Question about cardinal numbers ==
I was about to edit {{Z|Z16435}} to add my function {{Z|Z34308}}, but I noticed that none of the other functions have a gender parameter.
Should I create a new wrapper function "Bulgarian cardinal, neuter", or should I just remove the gender parameter and always return neuter? <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 10:39, 28 April 2026 (UTC)
:The “cardinal” functions should return the words used for “counting” numbers in the abstract.
:We should consider converting them to return a {{Z|Z11}} rather than a {{Z|Z6}}. It may even be appropriate to return a {{Z|Z12}} to cater for language variants. Either way, I think that would be the approach to adopt for inflected forms, unless reference to specific lexeme-forms is required. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 12:52, 28 April 2026 (UTC)
::This. If a native of your language were to count up, which form would they be most likely to use? [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 13:29, 28 April 2026 (UTC)
::: {{re|GrounderUK|Feeglgeef}} Thanks for both your input!
::: I relabeled the aforementioned function to {{Z|Z34308}}, and created a new wrapper function {{Z|Z34457}}.
::: Should I specify that my old function is a monolingual text in parentheses? <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 16:26, 28 April 2026 (UTC)
::::You don't have to, unless you think that is something that would require distinction when viewing the function in a list of search results &c. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 16:36, 28 April 2026 (UTC)
== Optional/nullable function parameters ==
Hello!
Recently, I was informed that Wikifunctions has no optional/nullable function parameters as of now.
Are there any future plans to support this, and/or workarounds? Maybe create a union type system like "{{Z|6}} or {{Z|23}}". <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 17:53, 28 April 2026 (UTC)
:What I do for this is use an "is empty" function corresponding to the type of the parameter in an If statement. If it isn't empty, the function works as intended. Otherwise, it does something else. [[User:JJPMaster|JJP]]<sub>[[User talk:JJPMaster|Mas]]<sub>[[Special:Contributions/JJPMaster|ter]]</sub></sub> ([[wikt:she|she]]/[[wikt:they|they]]) 17:58, 28 April 2026 (UTC)
:Unions are not a thing (yet) on Wikifunctions, but you can always define an argument of type {{Z|Z1}}, which means that all types are allowed (I already did this for {{Z|Z26737}}; note that it is still a ugly workaround, don't use it for high level functions). Also, note that usually on Wikifunctions we use {{Z|Z24}} as the null value. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 18:00, 28 April 2026 (UTC)
:: {{re|JJPMaster|Dv103}} Thanks for your help!
:: @[[User:Dv103|Dv103]] told me a function call with a missing parameter is treated as an invalid function call, so how does the "is empty" function work with that?
:: Also, setting the type to {{Z|1}} seems naive, like setting the type as <code>any</code> in TypeScript...
:: Related question: Are there plans to add default values to parameters (outside of "if empty")? <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 18:19, 28 April 2026 (UTC)
:::Setting the type to {{Z|Z1}} is actually naive, and that's why I advised you to only use it for low-level functions. Currently there is nothing better. Sometimes, type correctness is not actually checked, so it might seem that nullable types are possible. But it is still an hack, and it could broke anytime since it is not intended behavior.
:::I don't think that there are current plans to add default values (but correct me if I'm wrong). The closest thing that comes to my mind is that, if you incorporate Wikifunctions into Wikitext, you can leave empty some fields (only of some specific types) and Parsoid will replace them to their default value. This is done only depending on the type, and not on the functions. For example, {{Z|Z6091}} and {{Z|Z6001}} are assigned the QID associated to the page, and {{Z|Z20420}} is assigned the current date. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 18:56, 28 April 2026 (UTC)
:::@[[User:QuickQuokka|QuickQuokka]]: At the very least, [[Z10008]] accepts a null input. Maybe that feature is unique to the String type—I am not sure. [[User:JJPMaster|JJP]]<sub>[[User talk:JJPMaster|Mas]]<sub>[[Special:Contributions/JJPMaster|ter]]</sub></sub> ([[wikt:she|she]]/[[wikt:they|they]]) 19:10, 28 April 2026 (UTC)
::::I think it's just not checked, but it shouldn't be intended. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 19:20, 28 April 2026 (UTC)
::::Strings and typed lists can be “empty” in the sense that their length can be zero. Typed pairs may also be “empty” in a degenerate sense, but such an object will not be returned from a code implementation. A typed map with no entries will also fail to be returned from code, although it is fine in compositions.
::::For a genuinely optional parameter, I prefer a properly typed list, which at least encourages an argument of the correct type. {{Z|Z813}} is also typically faster than {{Z|Z10008}}. Quite a good example of this approach is {{Z|Z23723}}, where it helps to resolve the type union (using [[Z1]]) for both Z6003K1 and Z6003K3. Of course, there’s nothing to prevent more than one element in the list, but additional elements are easily ignored. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 22:55, 28 April 2026 (UTC)
:Pinging {{ping|Jdforrester (WMF)|prefix=|p=}}, I believe there are no current plans. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 19:02, 28 April 2026 (UTC)
::@[[User:QuickQuokka|QuickQuokka]]: I'm afraid there are no current plans to build out optional params, indeed; we would be happy to review this if a compelling case was made, but it'd be a lot of work to re-build the [[Wikifunctions:Function model|function model]] with that support and ensure we don't break (too many) things. [[User:Jdforrester (WMF)|Jdforrester (WMF)]] ([[User talk:Jdforrester (WMF)|talk]]) 19:11, 28 April 2026 (UTC)
== Legacy functions ==
If and when more robust methods of abstractly representing and generating linguistic content come around, and more efficient ways of creating abstract content are devised and implemented, I suspect that our current methods will require some form of deprecation. This is a significant source of concern for me in relation to WF and AW, questioning how prone our current methods of doing things are and eventually will be prone to obsolescence, and how it will be worked around when it comes. We have over 1 250 articles on AW presently, and these are rather all over the place. I suspect the maintenance burden from keeping these articles up to code will eventually, err, creep up on us, I suppose, and some kind of major refactoring will be necessary. We are definitely in a period of experimentation and whatnot right now but eventually, like with enwiki, some sort of structure and rigor will form and I suspect it will start to become rather boring for me...
I, for one, very much enjoy experimenting with new and better ways to do things here. I don't personally mind changing things to use a new and better paradigm if need be, that sort of thing highly excites me, but of course there will be things that are left behind, and I suspect maybe bots will be employed to deal with this? A lot of Wikipedia sister sites seem to do that, e.g. going and fixing up use of deprecated templates. Considering the nature of Wikifunctions and Abstract Wikipedia I suspect certain maintenance tasks will be made simpler or even trivial by the typical uniformity of our implementations.
I guess I am just concerned if Wikifunctions or Abstract Wikipedia will ever accrue its own kind of "technical debt" with how we are plowing through things presently, and if there is a plan for how we will eventually seek to mitigate that. Maybe too early to ask this question, but I am a notoriously anxious person, so I thought it wouldn't hurt to raise the question regardless... — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 21:30, 26 April 2026 (UTC)
:I very much agree, thank you for expressing my position so well. {{ping|Immanuelle}} has been using an AI-generated tool (well, they haven't edited in a week, perhaps it's a break or perhaps they don't wish to contribute to the project any more) to create a bunch of articles en masse, which I have warned them multiple times is a bad idea (on top of evolving functions, all of the articles are one-sentence-per-paragraph, [[abstract:Q12184|like so]]). That's why I've been avoiding creating articles recently, I'd say I have a good fourth (no data to support, rough guess) of the comments on the wiki, yet less than a percentage of the article count (only three, including the [[abstract:Q319|first article]], though, so perhaps I'm the next [[w:Special:Permalink/908493298|office.bomis.com]]). [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 19:41, 27 April 2026 (UTC)
:: I feel that the overwhelming presence of these low-quality articles (which I admit I myself am [[abstract:Q1710970|guilty]]/[[abstract:Q7601858|of]]/[[abstract:Q39338|creating]], usually as testbeds) may incur a large maintenance burden. I do expect them to be easy to detect, however, as searching for the presence of "deprecated" NLG functions is trivial, and it is possible that replacing them with their future ''even abstracter™'' counterparts could be done automatically since they all have the same signatures and can be expected to create the same form of sentence. If it needs to be done manually for a while or for certain delinquent instances, my hope is that it will be fun, at least for a while.
:: I just hope that these hypothetical future waves of "this new and versatile way of abstractly representing linguistic content" obsoleting previous methods and requiring refactoring across all articles is only a one-time thing. We should strive to be as robust and flexible as possible from the outset as each brand new paradigm of abstraction is also a brand new maintenance burden for updating old articles. At the end of the day, at least ''some'' of these articles will still render to many different languages even if their methods of creating those sentences of theirs is completely outdated. Ergo, the time it takes for the switchover to be performed across our articles should not be a persistent inconvenience for users (as, of course, they will always still be able to read the content as it was before since these legacy functions aren't being deleted outright), and the increased availability that the new methods will bring about will likely act as motivation for them to join the effort in refactoring (「You're telling me that if I rewrite this article in this cool Lisp-looking stuff then I can probably read it in [[abstract:Q9307|Galician]]?? COOL!」). — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 20:26, 27 April 2026 (UTC)
:::Totally agree. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 02:54, 28 April 2026 (UTC)
:::My vague plan is to implement a default function returning an {{Z|Z89}}, for each language-neutral function. A single function would convert any of these to a {{Z|Z11}}, so that a composition of the two can be implemented as the current default until such time as the language-neutral function is ready to return a [[Z89]]. We can already convert a [[Z11]] to a [[Z89]] so, although there is more to be done in this space, existing language-specific functions could be adapted to return a [[Z89]] quite mechanistically.
:::Although we certainly could deliver parallel Z89 functions for each existing Z11 function, I don’t think we should assume that particular outcome. Provided the Z89 captures a lang attribute from the Z11’s language tag, the two representations should be largely interchangeable, although I am expecting a Z89 to carry additional attributes at the span level that would be lost on conversion to a Z11 (along with any higher-level tags and attributes).
:::When I say there is “more to be done in this space”, I am referring to a new type that would allow HTML fragments to be represented as tractable Wikifunctions objects, but this is currently drafted only in my head! [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 11:21, 29 April 2026 (UTC)
== [[Z34213]] ==
I'm not quite sure why this implementation is failing. Could someone take a look? [[User:JJPMaster|JJP]]<sub>[[User talk:JJPMaster|Mas]]<sub>[[Special:Contributions/JJPMaster|ter]]</sub></sub> ([[wikt:she|she]]/[[wikt:they|they]]) 02:24, 28 April 2026 (UTC)
:I've [https://phabricator.wikimedia.org/T419933#11863997 notified] the team that this is still occurring, the issue was marked as resolved. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 02:52, 28 April 2026 (UTC)
:Some useful tips:
:* create more testcases: sometimes it is a random error, so try to see how consistent it is between testcases
:* your implementation is very inefficient, since it fetches items and lexemes a lot of times. Ideally, each item and each lexeme should be only fetched once in all the execution tree.
:[[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 06:06, 28 April 2026 (UTC)
::Caching (''should?'') means that the lexeme and item data are cached, so the call doesn't actually execute multiple times. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 17:51, 28 April 2026 (UTC)
:::Are lexemes and items actually cached within the same function execution? Even if they are only partially fetched and/or fetched in bulk? [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 17:54, 28 April 2026 (UTC)
::::I don't have any evidence to prove that it works but that's definitely A. what's supposed to happen and B. the ideal behavior. This happens because the Z680X functions can be cached just like any other. [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 18:02, 28 April 2026 (UTC)
::::It is unclear. In general, I believe identical branches are resolved only once in orchestration, but there is also independent caching of Wikidata fetches.
::::According to @[[User:DMartin (WMF)|DMartin (WMF)]] ([https://t.me/Wikifunctions/30374 on Telegram]):
::::<blockquote>Well, no. We have caching of Wikidata entities that have been retrieved, but not of the results of nested function calls. There is a proposal for doing this in the context of the V2 composition language, when it's a bit more mature, and it's regarded as a relatively high priority.</blockquote>
::::It’s hard to tell whether fetches in nested calls are, in fact, cached and available for other nested calls in the same call, since it is not generally the actual fetch that consumes the most resources. Rather (I believe), it is construction and transmission of the result object, which is currently repeated afresh in each nested call (unless it is in an identical branch).
::::I hope that’s clear, and I apologise in advance if it happens to be inaccurate! [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 10:16, 29 April 2026 (UTC)
:::::Oh, I should clarify. There is a lot of caching going on, in several different places. Lexemes and items ''are'' cached by the orchestrator within the same function execution, even if they are only partially fetched and/or fetched in bulk. When I said that we don't have caching of the results of nested function calls, I meant that's not happening in general, for all nested function calls in compositions. But fetching of Wikidata entities gets special treatment, so yes, fetched content from Wikidata is cached, regardless if it was fetched by a top-level call or a nested call.
:::::It is also true that the construction of a ZObject from the fetched JSON might happen more than once within the same function execution, depending on how a composition has been structured. However, the construction of the ZObject is actually very fast, compared to the elapsed time of getting the JSON from Wikidata. [[User:DMartin (WMF)|DMartin (WMF)]] ([[User talk:DMartin (WMF)|talk]]) 18:04, 1 May 2026 (UTC)
== Filtering types of objects ==
Hello!
I have tried to comb through my own edit history several times, but it's really hard to search for specifics because there's no differentiation between different types of objects (functions, implementations, tests, etc.) in the logs as far as I can tell.
Am I missing anything? I want it to work sort of like how filtering by namespace works. <span style="border-radius:99q;padding:0 7q;background:#103;border:3q solid #FBF">[[User:QuickQuokka|<span style="color:#FBF">'''QuickQuokka'''</span>]]</span> <sup>[[[User talk:QuickQuokka|talk]] • [[Special:Contribs/QuickQuokka|contribs]]]</sup> 16:48, 27 April 2026 (UTC)
:There is differentiation, it's just rather hard to look through. Since all ZObjects are just JSON data at their core, you can search for instances of <code>{ "Z1K1": "Z[type]"</code>. I haven't tried this so I'm not sure how well it would work and I know MediaWiki search syntax treats quotation marks as a special character, but I have seen Wikifunctions pages link to searches using this before. There is also [[Special:ListObjectsByType]] but it is sitewide rather than specific to your edit history in particular. — [[User:Theki|rae<sup>5e</sup>]] <[[User talk:Theki|talk]]> 18:59, 27 April 2026 (UTC)
::''[It doesn’t help directly here, but please see [[WF:Find]] for more details of how this works.]'' [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 10:24, 29 April 2026 (UTC)
:See the feature requests [[phab:T399244]]/[[phab:T373735]]. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 06:06, 28 April 2026 (UTC)
:The lack of filtering edits by namespace is exactly the problem that I was trying to solve with the [[User:Amire80/wikifunctionsanalytics]] tool.
:I even kind of succeeded, but it has two major problems:
:# It doesn't have any real frontend, so you have to know some SQL to use it (or ask other people who know SQL).
:# It doesn't get information from the live site, but from the dump, which appears to be updated once a month.
:I've made a [https://quarry.wmcloud.org/query/104794 sample query for you]. Unfortunately, it won't do anything at the moment because of the second problem—your edits started in April 2026, which isn't over yet, so the dump for it hasn't been processed. But I hope that early in May you'll be able to use the same query and see something useful.
:(I plan to add support for recent edits, but I haven't done it yet. Now that I more or less figured out how to process Wikifunctions edits, I'm focused on trying to understand Abstract Wikipedia edits. Processing up-to-date edits from both sites will possibly be the next thing I work on, but if you know some Python and want to try doing it yourself, don't wait for me—[https://gitlab.wikimedia.org/toolforge-repos/wikifunctions-analytics Patches welcome].) [[User:Amire80|Amir E. Aharoni]] ([[User talk:Amire80|talk]]) 18:51, 28 April 2026 (UTC)
::@[[User:QuickQuokka|QuickQuokka]], I've just updated the data until the end of April. Now the query to which I linked above gives some results. You can also try running other queries if you know SQL. (Or try asking for other queries if you don't.) [[User:Amire80|Amir E. Aharoni]] ([[User talk:Amire80|talk]]) 03:26, 3 May 2026 (UTC)
== Z6830 for Chinese ==
I was trying to use {{Z|Z6830}} for implementation in the Chinese-language. And turns out most of the Lexeme on Wikidata is using [[d:Q727694]] as the language instead of [[d:Q7850]]. This makes it impossible to use the mentioned function above, since Standard Chinese is not available (or did I miss something?). Is there a way to fetch lexemes with language=[[d:Q727694]] from item? [[User:Sun8908|Sun8908]] ([[User talk:Sun8908|talk]]) 18:20, 30 April 2026 (UTC)
:@[[User:Sun8908|Sun8908]] There is [[Z1006]] for Chinese and it has the language code zh. There is an overview of languages in [[Module:Wikifunctions label]] so you can search there for chinese versions and choose the one you need. [[User:Hogü-456|Hogü-456]] ([[User talk:Hogü-456|talk]]) 20:53, 5 May 2026 (UTC)
::I know that. The problem is when using the function [[Z6830]], it cannot retrieve lexeme with language [[d:Q727694]] (but it is the "Chinese language" with the most current Wikidata lexemes, see [https://ordia.toolforge.org/language/ ordia]). I think it should be a Wikidata problem, I might fix it (possibly by creating the same lexemes with language code zh) on Wikidata. Thanks anyway. [[User:Sun8908|Sun8908]] ([[User talk:Sun8908|talk]]) 05:39, 6 May 2026 (UTC)
:Could you provide an example of a Chinese lexeme that has a linked Wikidata item, or a Z6830 function call that fails to find such a lexeme where one exists? [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 07:55, 6 May 2026 (UTC)
::Here: [[d:Lexeme:L846083]]. I think that's a primary reason of me trying to look into this problem, as the label in zh for [[d:Q6256]] (country) is not a single phrase (see its talk page on WD for more information). This makes some Abstract Wikipedia articles very weird in Chinese when {{Z|Z26570}} is used, so lexeme could potentially fix that. [[User:Sun8908|Sun8908]] ([[User talk:Sun8908|talk]]) 10:33, 6 May 2026 (UTC)
:::Thank you. It looks as though {{Z|Z6830}} [https://www.wikifunctions.org/view/en/Z6830?call=%7B%22Z1K1%22%3A%22Z7%22%2C%22Z7K1%22%3A%22Z6830%22%2C%22Z6830K1%22%3A%7B%22Z1K1%22%3A%22Z6091%22%2C%22Z6091K1%22%3A%22Q6256%22%7D%2C%22Z6830K2%22%3A%7B%22Z1K1%22%3A%22Z6092%22%2C%22Z6092K1%22%3A%22P5137%22%7D%2C%22Z6830K3%22%3A%7B%22Z1K1%22%3A%22Z60%22%2C%22Z60K1%22%3A%22cmn%22%2C%22Z60K2%22%3A%5B%22Z6%22%5D%7D%7D returns that lexeme for language tag "cmn"]. Perhaps that tag should be added into the helpers for {{Z|Z24144}}? If it is widely used for lexemes, perhaps it should have its own {{Z|Z60}}? In any event, improvements might be considered under [[:phab:T390563]] (or otherwise), including amending [[Z6830]] to also consider "cmn" (and "zho", "chi"…?) when requests are made for "zh-hans" or "zho-hant" (or others?) @[[User:Winston Sung|Winston Sung]] @[[User:DMartin (WMF)|DMartin (WMF)]] [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 17:22, 6 May 2026 (UTC)
::::If you go to [[d:Special:NewLexeme]] and put in [[d:Q727694]] as the language, it is going to tell you it has an unrecognized language code. So I believe "cmn" should not be a {{Z|Z60}} by default? I also started [[d:Wikidata:Project_chat#Lexemes_with_language_Standard_Chinese_(Q727694)|a discussion on WD]] regarding this. I guess we can still use it as a fallback language though if possible. [[User:Sun8908|Sun8908]] ([[User talk:Sun8908|talk]]) 03:43, 7 May 2026 (UTC)
::::We don't have a separated <code>cmn</code> BCP 47 language subtag in MediaWiki and Wikidata at the moment. <code>zho</code> and <code>chi</code> are ISO 639 language codes but not BCP 47 language subtags.
::::For Modern Standard Mandarin, please use <code>zh-*</code> language tags for now. -- [[User:Winston Sung|Winston Sung]] ([[User talk:Winston Sung|talk]]) 15:26, 8 May 2026 (UTC)
1ik179ieeuf1a1xqga6n5wceesylehk
User talk:Rachmat04/Archives/2026
3
82370
281658
277053
2026-06-08T03:08:09Z
SpBot
978
archiving 1 section from [[User talk:Rachmat04]] (after section [[User talk:Rachmat04/Archives/2026#Wikifunctions_&_Abstract_Wikipedia_Newsletter_#247_is_out:_References_from_Wikidata_now_available|Wikifunctions_&_Abstract_Wikipedia_Newsletter_#247_is_out:_References_from_Wikidata_now_available]])
281658
wikitext
text/x-wiki
{{Talkarchive}}
== Wikifunctions & Abstract Wikipedia Newsletter #242 is out: Request for Discussion: Syntactic tables ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-04-02|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we introduce a proposal for Natural Language Generation, we introduce a page for function suggestions from Abstract Wikipedia, we inform you that there will be a presentation about Abstract Wikipedia at WikiCon Australia, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Also, we remind you that if you have questions or ideas to discuss, the next '''Volunteers' Corner''' will be held on '''[https://zonestamp.toolforge.org/1776101400 April 13, at 17:30 UTC]''' ([https://meet.google.com/xuy-njxh-rkw link to the meeting]).
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 13:37, 3 April 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
== Wikifunctions & Abstract Wikipedia Newsletter #243 is out: Community proposals for capturing meaning ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-04-10|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we report on three community proposals on syntactic approaches, we introduce a new Type (Complex numbers), we report on current hiccups on Abstract Wikipedia, we share more information about a presentation about Abstract Wikipedia at WikiCon Australia, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Also, we remind you that if you have questions or ideas to discuss, the next '''Volunteers' Corner''' will be held on '''[https://zonestamp.toolforge.org/1776101400 April 13, at 17:30 UTC]''' ([https://meet.google.com/xuy-njxh-rkw link to the meeting]).
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 15:35, 10 April 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
== Wikifunctions & Abstract Wikipedia Newsletter #244 is out: Milestones; Some major issues hopefully resolved ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-04-16|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we celebrate 4000 functions on Wikifunctions and 1000 abstract articles on Abstract Wikipedia, we announce that we should have fixed some major issues with the websites, we inform you on our latest outreach activities, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 10:22, 17 April 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
== Wikifunctions & Abstract Wikipedia Newsletter #245 is out: The Foundation's search for the perfect language ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-04-25|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we present an academic paper about Abstract Wikipedia, we discuss our latest Type created, and we take a look at the newest created functions.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 09:54, 25 April 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
== Wikifunctions & Abstract Wikipedia Newsletter #246 is out: Request for input: what should we count for Abstract Wikipedia ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-05-02|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we ask you what would be the relevant metrics for Abstract Wikipedia, we discuss our latest news on Composition Language v2, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 12:21, 2 May 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
== Wikifunctions & Abstract Wikipedia Newsletter #247 is out: References from Wikidata now available ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-05-08|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we announce that is now possible to pass references in Wikidata statements, we introduce the [https://abstract-data.toolforge.org/ Abstract Data dashboard], we report you on the presentation about Abstract Wikipedia at WikiCon Australia, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Also, we remind you that if you have questions or ideas to discuss, the next '''Volunteers' Corner''' will be held on '''[https://zonestamp.toolforge.org/1778520600 May 11, at 17:30 UTC]''' ([https://meet.google.com/xuy-njxh-rkw link to the meeting]).
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 11:16, 8 May 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
q1pnjsbsidl4qzprlwp7xaq45pdw85r
Talk:Z60
1
83381
281626
279986
2026-06-07T20:41:34Z
YoshiRulz
10156
Squish whitespace in cardinality field
281626
wikitext
text/x-wiki
{{type documentation|Z60|Natural language
|cardinality=Valid: on the order of {{formatnum:100000}}, counting every meaningful combination of language tags and subtags assigned by IANA<br>Type admits: arbitrarily many<br>Persistent: {{formatnum:{{NUMBEROFLANGUAGES}}}}
}}
mx1df5068piokkeoc3gajcx1ohw36c9
Talk:Z61
1
83559
281624
279987
2026-06-07T20:38:21Z
YoshiRulz
10156
Squish whitespace in cardinality field
281624
wikitext
text/x-wiki
{{type documentation|Z61|Programming language
|cardinality=Valid: {{formatnum:2}} (the rest are unused)<br>Type admits: arbitrarily many<br>Persistent: around {{formatnum:20}}
}}
4w2sm96fo78po8dxezxr5xw0fkyx5pq
User:YoshiRulz/Type documentation/search table
2
84322
281629
279388
2026-06-07T20:47:42Z
YoshiRulz
10156
Add regular ListObjectsByType
281629
wikitext
text/x-wiki
<onlyinclude><includeonly>* [[Special:WhatLinksHere/{{{1}}}]] (all links from all namespaces, misses any references from Implementations-by-code)
* [[Special:Search/: "{{{1}}}"|Search for <code>"{{{1}}}"</code> in mainspace]] (includes references from Implementations-by-code)
* [[Special:ListObjectsByType/{{{1}}}]] (persistent objects whose Type is declared as {{{1}}})
* {{#tag:{{#ifeq:{{{1}}}|Z1|s|span}}|[https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&return_type={{{1}}}&orderby=oldest Special:ListObjectsByType/Z8/{{{1}}}] (functions whose return Type is declared as {{{1}}})|class=plainlinks}}
{| class="mw-collapsible mw-collapsed wikitable" style="width: 100%;"
! colspan="3" | Queries for {{Z|8}} declarations
|-
! style="text-align: end;" | (…)
! style="text-align: start;" | → {{{1}}}
| [[Special:Search/: "Z8K2 {{{1}}}"|Search for <code>{{nowrap|"Z8K2 {{{1}}}"}}</code> in mainspace]]
|-
! style="text-align: end;" | {{nowrap|({{{1}}}, …)}}
! style="text-align: start;" | → …
| [[Special:Search/: "Z8K1 Z17 Z1K1 Z17 Z17K1 {{{1}}}"|Search for <code>{{nowrap|"Z8K1 Z17 Z1K1 Z17 Z17K1 {{{1}}}"}}</code> in mainspace]]
|-
! style="text-align: end;" | {{nowrap|(¬{{{1}}}, …, {{{1}}}, …)}}
! style="text-align: start;" | → …
| [[Special:Search/: "Z17K1 {{{1}}}" -"Z8K1 Z17 Z1K1 Z17 Z17K1 {{{1}}}"|Search for <code>{{nowrap|"Z17K1 {{{1}}}"}} {{nowrap|-"Z8K1 Z17 Z1K1 Z17 Z17K1 {{{1}}}"}}</code> in mainspace]]
|-
! style="text-align: end;" | {{nowrap|(…, {{{1}}}, …)}}
! style="text-align: start;" | → {{{1}}}
| [[Special:Search/: "Z8K2 {{{1}}}" "Z17K1 {{{1}}}"|Search for <code>{{nowrap|"Z8K2 {{{1}}}"}} {{nowrap|"Z17K1 {{{1}}}"}}</code> in mainspace]]
|-
! colspan="3" |
|-
! style="text-align: end;" | (…)
! style="text-align: start;" | {{nowrap|→ <{{{1}}}>[…]}}
| [[Special:Search/: "Z8K2 Z1K1 Z7 Z7K1 Z881 Z881K1 {{{1}}}"|Search for <code>{{nowrap|"Z8K2 Z1K1 Z7 Z7K1 Z881 Z881K1 {{{1}}}"}}</code> in mainspace]]
|-
! style="text-align: end;" | {{nowrap|(…, <{{{1}}}>[…], …)}}
! style="text-align: start;" | → …
| [[Special:Search/: "Z17K1 Z1K1 Z7 Z7K1 Z881 Z881K1 {{{1}}}"|Search for <code>{{nowrap|"Z17K1 Z1K1 Z7 Z7K1 Z881 Z881K1 {{{1}}}"}}</code> in mainspace]]
|-
! style="text-align: end;" | {{nowrap|(…, <{{{1}}}>[…], …)}}
! style="text-align: start;" | {{nowrap|→ <{{{1}}}>[…]}}
| [[Special:Search/: "Z8K2 Z1K1 Z7 Z7K1 Z881 Z881K1 {{{1}}}" "Z17K1 Z1K1 Z7 Z7K1 Z881 Z881K1 {{{1}}}"|Search for <code>{{nowrap|"Z8K2 Z1K1 Z7 Z7K1 Z881 Z881K1 {{{1}}}"}} {{nowrap|"Z17K1 Z1K1 Z7 Z7K1 Z881 Z881K1 {{{1}}}"}}</code> in mainspace]]
|-
! colspan="3" | More info at [[WF:Find#Finding_functions|WF:Find]]<!--
--><span style="float: inline-end;">{{mini navbar|User:YoshiRulz/Type documentation/search table}}</span>
|}</includeonly></onlyinclude>
Preview:
{{type documentation|Z99|Quote}}
jnh8bh2n4igrih1pj88p1x7mjzlro8j
Wikifunctions:Project chat/Archive/2026/05
4
84806
281656
280947
2026-06-08T03:08:06Z
SpBot
978
archiving 1 section from [[Wikifunctions:Project chat]] (after section [[Wikifunctions:Project chat/Archive/2026/05#Wikifunctions_&_Abstract_Wikipedia_Newsletter_#247_is_out:_References_from_Wikidata_now_available|Wikifunctions_&_Abstract_Wikipedia_Newsletter_#247_is_out:_References_from_Wikidata_now_available]])
281656
wikitext
text/x-wiki
{{Talkarchive}}
== Key not found error ==
Is there a reason why I am getting key not found error for this [[Z34677|function]] {{Z|Z34677}}? All the underlying functions run and all the test cases work. The debug information does not give more details. Any pointers? Thanks in advance [[User:Jsamwrites|John Samuel]] 19:24, 1 May 2026 (UTC)
:It was passing the [[Z6091]] to {{Z|34641}} when that takes a [[Z6001]]. I've fixed that, but there's some other problem with the logic, so I've left it disconnected. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 19:42, 1 May 2026 (UTC)
::@[[User:YoshiRulz|YoshiRulz]] Thanks a lot. [[User:Jsamwrites|John Samuel]] 20:21, 1 May 2026 (UTC)
== Wikifunctions & Abstract Wikipedia Newsletter #246 is out: Request for input: what should we count for Abstract Wikipedia ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-05-02|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we ask you what would be the relevant metrics for Abstract Wikipedia, we discuss our latest news on Composition Language v2, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 12:21, 2 May 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
== Any formal process for deletion of pages ==
Does a formal process exist for the deletion of functions, implementations, and tests that includes a notification system for creators, analogous to Wikidata’s process, explaining the rationale behind the deletion (or proposal for deletion)? [[User:Jsamwrites|John Samuel]] 12:36, 3 May 2026 (UTC)
:Does [[Wikifunctions:Requests for deletions]] work? [[User:Amire80|Amir E. Aharoni]] ([[User talk:Amire80|talk]]) 13:00, 3 May 2026 (UTC)
:Please see the discussion at [[Wikifunctions talk:Requests for deletions#Should we expect Objects' creators to get pinged on deletion proposals?]].
:As I see it, it is the proposer’s responsibility to consult appropriately before making a request and we expect our administrators to act only when satisfied that appropriate consultation has occurred. In many cases, no consultation is required. Administrators may delete their own contributions without making a request, but this is not a practice I would encourage. [[User:GrounderUK|GrounderUK]] ([[User talk:GrounderUK|talk]]) 13:51, 3 May 2026 (UTC)
== Nested functions in compositions ==
I wish it will be easier to a add another function about a specific existing function in a function implementation based on a composition. When I write long functions in spreadsheets I usually stat with a small part and then I try to go further and after important steps I test if the output is as expected. I created [[Z34826]] to get the German gender specific occupation lexeme for a specific person based on their gender. I wanted to add a function around the existing one and it was not successful. It is not very easy to implement as it requires the possibily to move a part to another section but I think it can be helpful if it will be implemented. So far I spend more time as expected on the function. Describing it with words what the function needs to do is much easier than implementing it here in Wikifunctions. So I think there needs to be improvement to make Wikifunctions more accessible. [[User:Hogü-456|Hogü-456]] ([[User talk:Hogü-456|talk]]) 21:10, 5 May 2026 (UTC)
:Have you tried to use the copy-paste functionality? It is very useful to move parts of composition arounn. [[User:Dv103|Dv103]] ([[User talk:Dv103|talk]]) 07:12, 6 May 2026 (UTC)
:I've also found the composition editor to be wholly unsuitable for any expressions more than a few levels deep. (Even with the <code>localStorage</code> clipboard, because of its overzealous type checks.) Compositions naturally grow out from the "leaves", the immediate operations on the inputs, while the interface really wants you to build from the "root". I mostly use the [https://yoshirulz.gitlab.io/WikiLambdaBlockly drag-and-drop block editor] which I made to smooth over some of the site's problems, so if you want to try that out and give me some feedback I'd appreciate it. [[User:YoshiRulz|YoshiRulz]] ([[User talk:YoshiRulz|talk]]) 14:36, 6 May 2026 (UTC)
== Wikifunctions & Abstract Wikipedia Newsletter #247 is out: References from Wikidata now available ==
There is [[:f:Special:MyLanguage/Wikifunctions:Status updates/2026-05-08|a new update]] for Abstract Wikipedia and Wikifunctions. Please, come and read it!
In this issue, we announce that is now possible to pass references in Wikidata statements, we introduce the [https://abstract-data.toolforge.org/ Abstract Data dashboard], we report you on the presentation about Abstract Wikipedia at WikiCon Australia, and we take a look at the latest software developments.
Want to catch up with the previous updates? Check [[:f:Special:MyLanguage/Wikifunctions:Status updates|our archive]]!
Also, we remind you that if you have questions or ideas to discuss, the next '''Volunteers' Corner''' will be held on '''[https://zonestamp.toolforge.org/1778520600 May 11, at 17:30 UTC]''' ([https://meet.google.com/xuy-njxh-rkw link to the meeting]).
Enjoy the reading! -- [[User:Sannita (WMF)|User:Sannita (WMF)]] ([[User talk:Sannita (WMF)|talk]]) 11:16, 8 May 2026 (UTC)
<!-- Message sent by User:Sannita (WMF)@metawiki using the list at https://meta.wikimedia.org/w/index.php?title=Global_message_delivery/Targets/Wikifunctions_%26_Abstract_Wikipedia&oldid=30325620 -->
4sdk42znfrcxm391w9pljukbynysrgi
Wikifunctions:Status updates/2026-06-05/de
4
85129
281446
281105
2026-06-07T18:41:33Z
Ameisenigel
44
Created page with "''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[$1|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[$2|99of9]] erstellt und von [[$3|Feeglgeef]] redaktionell bearbeitet. Danke!''"
281446
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
<div lang="en" dir="ltr" class="mw-content-ltr">
The functions [[Z36083|main articles]] and [[Z33842|main articles, complex]] are to be used on Abstract Wikipedia after a section heading to link to a separate article with more information about that subtopic. These links help the reader to quickly navigate to the topics they are most interested in, and are widely used across most language editions of Wikipedia. Our functions are modelled on the English Wikipedia [[w:Template:Main|Template:Main]], which takes a (usually short) set of articles to link to, optional alternative label strings for those links, and a parameter to denote and slightly change the behaviour of self-references. It returns a short inset referring to this link as the Main article for the subsection.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
The full version of our function, [[Z33842|main articles, complex]] is set up to accept three similar inputs and the language in which it should be rendered. The link items are entered as a list of [[Z6091|Wikidata item references]]. The alternate labels cannot simply be strings, because they would not work in all languages. For now, we accept a list of objects, but do not yet support any functionality on these objects. In the future they may be populated with function references or other data structures with the information required to generate alternate labels. The self-reference parameter is a Boolean. The function returns an [[Z89|HTML fragment]] as required for Abstract Wikipedia. The [[Z33847|composition implementation]] deals with wrapping each language's output with an HTML div with attributes <code>role="note" class="hatnote navigation-not-searchable"</code>, so the language sub-functions are just responsible for fetching and formatting the link name.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
Usually, the complexity of alternative labels and self-references is unnecessary. So the simpler version of our function, [[Z36083|main articles]], only requires a list of items and a language as arguments. It wraps the complex version, passing default values for the unused arguments. This version is simpler to use and read in Abstract Wikipedia source code.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
Six tests are available, based on the prolific use of this function to structure the abstract article [[:abstract:Q408|Australia (Q408)]]:
</div>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36085|''Main article:'' History of Australia]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36086|''Main articles:'' Geography of Australia and Australian continent]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] currently failing due to a tracked bug, [[phab:T427454|T427454]], affecting [[Z13464|Z13464]], which the composition relies on.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgian Dutch.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36088|''→ Hauptartikel:'' Klima in Australien]] awaiting an implementation in German.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36090|a test of the default function]] testing in Arabic, a right-to-left language, and successfully generating <code><span dir="rtl">← تاريخ أستراليا</span></code> with a link.</span>
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
8ib54y9f8j1pugx8wt026wcm0ok4rc3
281449
281446
2026-06-07T18:44:39Z
Ameisenigel
44
Created page with "Die Funktionen [[$1|Hauptartikel]] und [[$2|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an ..."
281449
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
<div lang="en" dir="ltr" class="mw-content-ltr">
The full version of our function, [[Z33842|main articles, complex]] is set up to accept three similar inputs and the language in which it should be rendered. The link items are entered as a list of [[Z6091|Wikidata item references]]. The alternate labels cannot simply be strings, because they would not work in all languages. For now, we accept a list of objects, but do not yet support any functionality on these objects. In the future they may be populated with function references or other data structures with the information required to generate alternate labels. The self-reference parameter is a Boolean. The function returns an [[Z89|HTML fragment]] as required for Abstract Wikipedia. The [[Z33847|composition implementation]] deals with wrapping each language's output with an HTML div with attributes <code>role="note" class="hatnote navigation-not-searchable"</code>, so the language sub-functions are just responsible for fetching and formatting the link name.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
Usually, the complexity of alternative labels and self-references is unnecessary. So the simpler version of our function, [[Z36083|main articles]], only requires a list of items and a language as arguments. It wraps the complex version, passing default values for the unused arguments. This version is simpler to use and read in Abstract Wikipedia source code.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
Six tests are available, based on the prolific use of this function to structure the abstract article [[:abstract:Q408|Australia (Q408)]]:
</div>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36085|''Main article:'' History of Australia]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36086|''Main articles:'' Geography of Australia and Australian continent]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] currently failing due to a tracked bug, [[phab:T427454|T427454]], affecting [[Z13464|Z13464]], which the composition relies on.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgian Dutch.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36088|''→ Hauptartikel:'' Klima in Australien]] awaiting an implementation in German.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36090|a test of the default function]] testing in Arabic, a right-to-left language, and successfully generating <code><span dir="rtl">← تاريخ أستراليا</span></code> with a link.</span>
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
c0n9o2j5e3emn0t0yju42w1cxvr8dod
281451
281449
2026-06-07T18:48:14Z
Ameisenigel
44
Created page with "Die Vollversion unserer Funktion [[$1|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[$2|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen je..."
281451
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
<div lang="en" dir="ltr" class="mw-content-ltr">
Usually, the complexity of alternative labels and self-references is unnecessary. So the simpler version of our function, [[Z36083|main articles]], only requires a list of items and a language as arguments. It wraps the complex version, passing default values for the unused arguments. This version is simpler to use and read in Abstract Wikipedia source code.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
Six tests are available, based on the prolific use of this function to structure the abstract article [[:abstract:Q408|Australia (Q408)]]:
</div>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36085|''Main article:'' History of Australia]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36086|''Main articles:'' Geography of Australia and Australian continent]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] currently failing due to a tracked bug, [[phab:T427454|T427454]], affecting [[Z13464|Z13464]], which the composition relies on.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgian Dutch.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36088|''→ Hauptartikel:'' Klima in Australien]] awaiting an implementation in German.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36090|a test of the default function]] testing in Arabic, a right-to-left language, and successfully generating <code><span dir="rtl">← تاريخ أستراليا</span></code> with a link.</span>
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
sr54agsu3gxeeywjaq87b2zlanibo4v
281454
281451
2026-06-07T18:59:47Z
Ameisenigel
44
Created page with "Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[$1|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen."
281454
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
<div lang="en" dir="ltr" class="mw-content-ltr">
Six tests are available, based on the prolific use of this function to structure the abstract article [[:abstract:Q408|Australia (Q408)]]:
</div>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36085|''Main article:'' History of Australia]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36086|''Main articles:'' Geography of Australia and Australian continent]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] currently failing due to a tracked bug, [[phab:T427454|T427454]], affecting [[Z13464|Z13464]], which the composition relies on.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgian Dutch.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36088|''→ Hauptartikel:'' Klima in Australien]] awaiting an implementation in German.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36090|a test of the default function]] testing in Arabic, a right-to-left language, and successfully generating <code><span dir="rtl">← تاريخ أستراليا</span></code> with a link.</span>
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
tsrughmkzj5qzqkty1g5v9ux7bxr8hy
281456
281454
2026-06-07T19:00:05Z
Ameisenigel
44
Created page with "Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[$1|Australien (Q408)]] basieren:"
281456
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36085|''Main article:'' History of Australia]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36086|''Main articles:'' Geography of Australia and Australian continent]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] currently failing due to a tracked bug, [[phab:T427454|T427454]], affecting [[Z13464|Z13464]], which the composition relies on.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgian Dutch.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36088|''→ Hauptartikel:'' Klima in Australien]] awaiting an implementation in German.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36090|a test of the default function]] testing in Arabic, a right-to-left language, and successfully generating <code><span dir="rtl">← تاريخ أستراليا</span></code> with a link.</span>
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
0ru6bwcw6u429i2zi9ppub9nygrdkck
281458
281456
2026-06-07T19:00:34Z
Ameisenigel
44
Created page with "[[$1|''Main article:'' History of Australia]]"
281458
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* [[Z36085|''Main article:'' History of Australia]]
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36086|''Main articles:'' Geography of Australia and Australian continent]]</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] currently failing due to a tracked bug, [[phab:T427454|T427454]], affecting [[Z13464|Z13464]], which the composition relies on.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgian Dutch.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36088|''→ Hauptartikel:'' Klima in Australien]] awaiting an implementation in German.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36090|a test of the default function]] testing in Arabic, a right-to-left language, and successfully generating <code><span dir="rtl">← تاريخ أستراليا</span></code> with a link.</span>
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
f0q5h0sz6vmj0r9bf08i11o7c64zyeg
281460
281458
2026-06-07T19:00:36Z
Ameisenigel
44
Created page with "[[$1|''Main articles:'' Geography of Australia and Australian continent]]"
281460
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* [[Z36085|''Main article:'' History of Australia]]
* [[Z36086|''Main articles:'' Geography of Australia and Australian continent]]
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] currently failing due to a tracked bug, [[phab:T427454|T427454]], affecting [[Z13464|Z13464]], which the composition relies on.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgian Dutch.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36088|''→ Hauptartikel:'' Klima in Australien]] awaiting an implementation in German.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36090|a test of the default function]] testing in Arabic, a right-to-left language, and successfully generating <code><span dir="rtl">← تاريخ أستراليا</span></code> with a link.</span>
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
gd8zk07ym3nbgslcn9g53iq1klztwj5
281462
281460
2026-06-07T19:01:25Z
Ameisenigel
44
Created page with "[[$1|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, $2, fehl, der [[$3|Z13464]] betrifft, worauf sich die Komposition verlässt."
281462
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* [[Z36085|''Main article:'' History of Australia]]
* [[Z36086|''Main articles:'' Geography of Australia and Australian continent]]
* [[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, [[phab:T427454|T427454]], fehl, der [[Z13464|Z13464]] betrifft, worauf sich die Komposition verlässt.
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgian Dutch.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36088|''→ Hauptartikel:'' Klima in Australien]] awaiting an implementation in German.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36090|a test of the default function]] testing in Arabic, a right-to-left language, and successfully generating <code><span dir="rtl">← تاريخ أستراليا</span></code> with a link.</span>
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
bm05pcfqo8yvdzyiqk041j4v7f6c1z0
281464
281462
2026-06-07T19:01:44Z
Ameisenigel
44
Created page with "[[$1|''Hoofdartikel:'' Geografie van Australië]] in Belgischem Niederländisch."
281464
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* [[Z36085|''Main article:'' History of Australia]]
* [[Z36086|''Main articles:'' Geography of Australia and Australian continent]]
* [[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, [[phab:T427454|T427454]], fehl, der [[Z13464|Z13464]] betrifft, worauf sich die Komposition verlässt.
* [[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgischem Niederländisch.
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36088|''→ Hauptartikel:'' Klima in Australien]] awaiting an implementation in German.</span>
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36090|a test of the default function]] testing in Arabic, a right-to-left language, and successfully generating <code><span dir="rtl">← تاريخ أستراليا</span></code> with a link.</span>
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
hhzziiwn38zc3sk2oby8o1ngvhq2j29
281466
281464
2026-06-07T19:02:03Z
Ameisenigel
44
Created page with "[[$1|''→ Hauptartikel:'' Klima in Australien]] wartet auf eine Implementierung in Deutsch."
281466
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* [[Z36085|''Main article:'' History of Australia]]
* [[Z36086|''Main articles:'' Geography of Australia and Australian continent]]
* [[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, [[phab:T427454|T427454]], fehl, der [[Z13464|Z13464]] betrifft, worauf sich die Komposition verlässt.
* [[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgischem Niederländisch.
* [[Z36088|''→ Hauptartikel:'' Klima in Australien]] wartet auf eine Implementierung in Deutsch.
* <span lang="en" dir="ltr" class="mw-content-ltr">[[Z36090|a test of the default function]] testing in Arabic, a right-to-left language, and successfully generating <code><span dir="rtl">← تاريخ أستراليا</span></code> with a link.</span>
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
6082u2ptdx7nsu07iifbhc7enabuj1f
281468
281466
2026-06-07T19:03:00Z
Ameisenigel
44
Created page with "[[$1|ein Test der Standardfunktion]], der in Arabisch testet, einer von rechts nach links geschriebenen Sprache, und erfolgreich $2 mit einem Link generiert."
281468
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* [[Z36085|''Main article:'' History of Australia]]
* [[Z36086|''Main articles:'' Geography of Australia and Australian continent]]
* [[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, [[phab:T427454|T427454]], fehl, der [[Z13464|Z13464]] betrifft, worauf sich die Komposition verlässt.
* [[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgischem Niederländisch.
* [[Z36088|''→ Hauptartikel:'' Klima in Australien]] wartet auf eine Implementierung in Deutsch.
* [[Z36090|ein Test der Standardfunktion]], der in Arabisch testet, einer von rechts nach links geschriebenen Sprache, und erfolgreich <code><span dir="rtl">← تاريخ أستراليا</span></code> mit einem Link generiert.
<div lang="en" dir="ltr" class="mw-content-ltr">
I chose this example as Function of the Week to encourage further language configuration. Supporting simple links with labels in a consistent scheme is a very clearly defined purpose, and is a reasonably easy starting point compared to other natural language functions. It is currently only [[Z33855|configured]] in English and Dutch, so further contributions would be welcome. It would also be interesting to discuss theoretical directions for the currently unused label-configuring argument.
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
7lt1pjue9kyhlqimtra3d5rfz0s8juq
281470
281468
2026-06-07T19:12:14Z
Ameisenigel
44
Created page with "Ich habe dieses Beispiel als Funktion der Woche ausgewählt, um zu weiterer Sprachkonfiguration zu ermutigen. Die Unterstützung einfacher Links mit Bezeichnungen nach einem einheitlichen Schema ist ein sehr klar definierter Anwendungszweck und stellt im Vergleich zu anderen Funktionen der natürlichen Sprache einen relativ einfachen Einstiegspunkt dar. Derzeit ist die Funktion nur für Englisch und Niederländisch [[$1|konfiguriert]], daher wären weitere Beiträge will..."
281470
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* [[Z36085|''Main article:'' History of Australia]]
* [[Z36086|''Main articles:'' Geography of Australia and Australian continent]]
* [[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, [[phab:T427454|T427454]], fehl, der [[Z13464|Z13464]] betrifft, worauf sich die Komposition verlässt.
* [[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgischem Niederländisch.
* [[Z36088|''→ Hauptartikel:'' Klima in Australien]] wartet auf eine Implementierung in Deutsch.
* [[Z36090|ein Test der Standardfunktion]], der in Arabisch testet, einer von rechts nach links geschriebenen Sprache, und erfolgreich <code><span dir="rtl">← تاريخ أستراليا</span></code> mit einem Link generiert.
Ich habe dieses Beispiel als Funktion der Woche ausgewählt, um zu weiterer Sprachkonfiguration zu ermutigen. Die Unterstützung einfacher Links mit Bezeichnungen nach einem einheitlichen Schema ist ein sehr klar definierter Anwendungszweck und stellt im Vergleich zu anderen Funktionen der natürlichen Sprache einen relativ einfachen Einstiegspunkt dar. Derzeit ist die Funktion nur für Englisch und Niederländisch [[Z33855|konfiguriert]], daher wären weitere Beiträge willkommen. Zudem wäre es interessant, theoretische Ansätze für das aktuell ungenutzte Argument zur Konfiguration von Bezeichnungen zu diskutieren.
<div lang="en" dir="ltr" class="mw-content-ltr">
=== Fresh Functions weekly: 63 new Functions ===
</div>
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
543z125zbvk4y9u87nstxw9so0e9359
281472
281470
2026-06-07T19:12:22Z
Ameisenigel
44
Created page with "=== Wöchentliche neue Funktionen: 63 neue Funktionen ==="
281472
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* [[Z36085|''Main article:'' History of Australia]]
* [[Z36086|''Main articles:'' Geography of Australia and Australian continent]]
* [[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, [[phab:T427454|T427454]], fehl, der [[Z13464|Z13464]] betrifft, worauf sich die Komposition verlässt.
* [[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgischem Niederländisch.
* [[Z36088|''→ Hauptartikel:'' Klima in Australien]] wartet auf eine Implementierung in Deutsch.
* [[Z36090|ein Test der Standardfunktion]], der in Arabisch testet, einer von rechts nach links geschriebenen Sprache, und erfolgreich <code><span dir="rtl">← تاريخ أستراليا</span></code> mit einem Link generiert.
Ich habe dieses Beispiel als Funktion der Woche ausgewählt, um zu weiterer Sprachkonfiguration zu ermutigen. Die Unterstützung einfacher Links mit Bezeichnungen nach einem einheitlichen Schema ist ein sehr klar definierter Anwendungszweck und stellt im Vergleich zu anderen Funktionen der natürlichen Sprache einen relativ einfachen Einstiegspunkt dar. Derzeit ist die Funktion nur für Englisch und Niederländisch [[Z33855|konfiguriert]], daher wären weitere Beiträge willkommen. Zudem wäre es interessant, theoretische Ansätze für das aktuell ungenutzte Argument zur Konfiguration von Bezeichnungen zu diskutieren.
<span id="Fresh_Functions_weekly:_63_new_Functions"></span>
=== Wöchentliche neue Funktionen: 63 neue Funktionen ===
<div lang="en" dir="ltr" class="mw-content-ltr">
This week we had 63 new functions. Here is an incomplete list of functions with implementations and passing tests to get a taste of what functions have been created. Thanks everybody for contributing!
</div>
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
oepdv4s7q5v00nom96qih872c4dhhsi
281474
281472
2026-06-07T19:12:29Z
Ameisenigel
44
Created page with "Diese Woche hatten wir 63 neue Funktionen. Hier ist eine unvollständige Liste von Funktionen mit Implementierungen und bestandenen Tests, um einen Eindruck davon zu bekommen, welche Funktionen erstellt wurden. Vielen Dank an alle für ihre Beiträge!"
281474
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* [[Z36085|''Main article:'' History of Australia]]
* [[Z36086|''Main articles:'' Geography of Australia and Australian continent]]
* [[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, [[phab:T427454|T427454]], fehl, der [[Z13464|Z13464]] betrifft, worauf sich die Komposition verlässt.
* [[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgischem Niederländisch.
* [[Z36088|''→ Hauptartikel:'' Klima in Australien]] wartet auf eine Implementierung in Deutsch.
* [[Z36090|ein Test der Standardfunktion]], der in Arabisch testet, einer von rechts nach links geschriebenen Sprache, und erfolgreich <code><span dir="rtl">← تاريخ أستراليا</span></code> mit einem Link generiert.
Ich habe dieses Beispiel als Funktion der Woche ausgewählt, um zu weiterer Sprachkonfiguration zu ermutigen. Die Unterstützung einfacher Links mit Bezeichnungen nach einem einheitlichen Schema ist ein sehr klar definierter Anwendungszweck und stellt im Vergleich zu anderen Funktionen der natürlichen Sprache einen relativ einfachen Einstiegspunkt dar. Derzeit ist die Funktion nur für Englisch und Niederländisch [[Z33855|konfiguriert]], daher wären weitere Beiträge willkommen. Zudem wäre es interessant, theoretische Ansätze für das aktuell ungenutzte Argument zur Konfiguration von Bezeichnungen zu diskutieren.
<span id="Fresh_Functions_weekly:_63_new_Functions"></span>
=== Wöchentliche neue Funktionen: 63 neue Funktionen ===
Diese Woche hatten wir 63 neue Funktionen. Hier ist eine unvollständige Liste von Funktionen mit Implementierungen und bestandenen Tests, um einen Eindruck davon zu bekommen, welche Funktionen erstellt wurden. Vielen Dank an alle für ihre Beiträge!
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
<span lang="en" dir="ltr" class="mw-content-ltr">A [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest complete list of all functions sorted by when they were created] is available.</span>
[[Category:Status updates{{#translation:}}|2026-06-05]]
3cpvwvsn05zc7vqe4hupgqgxnt150sj
281476
281474
2026-06-07T19:12:49Z
Ameisenigel
44
Created page with "Eine [$1 vollständige Liste aller Funktionen, sortiert nach ihrem Erstellungszeitpunkt], ist verfügbar."
281476
wikitext
text/x-wiki
<languages/>
{{Wikifunctions updates
| prevlabel = Vorheriges Update
| prev = 2026-05-30
| nextlabel = Nächstes Update
| next =
}}
<span id="The_illustrated_encyclopaedia"></span>
=== Die illustrierte Enzyklopädie ===
Wir freuen uns sehr, mitteilen zu können, dass ab dieser Woche die Möglichkeit besteht, Bilder zur Abstrakten Wikipedia hinzuzufügen.
Bilder sind ein wichtiger Bestandteil einer modernen Enzyklopädie. Sie können Textbeschreibungen ergänzen und funktionieren oft sprachübergreifend. Karten, Diagramme und Fotografien ermöglichen es, räumliche Beziehungen, Proportionen und Wechselwirkungen zu vermitteln sowie ein Thema zu veranschaulichen. Bilder können einen Wikipedia-Artikel lebendiger gestalten und die Aufnahme der Inhalte erleichtern.
Ein erstes Beispiel für eine Funktion, die ein HTML-Fragment erstellt, ist hier verfügbar: {{Z|Z36038}}. Ein Klick auf ein Bild führt zur Commons-Seite des Bildes, genau wie bei Wikipedia-Artikeln.
Derzeit unterstützen wir nur Bilder: keine Videos, Audiodateien, Karten, 3D-Modelle, Datentabellen oder Dokumente. Wir unterstützen jedoch eine Vielzahl von Bildformaten, einschließlich animierter PNGs und GIFs. Auch SVG-Bilder funktionieren, allerdings werden deren Animationen nicht angezeigt.
Die Beispielfunktion steht dir zum Ausprobieren zur Verfügung, und du kannst gerne weitere erstellen. Eine [[:mw:Special:MyLanguage/Help:Wikifunctions/Images|Dokumentation]] ist ebenfalls verfügbar — sie befindet sich zwar noch in Arbeit, deckt aber den aktuellen Umfang sowie die bekannten Einschränkungen dieser ersten Version ab. Eine Unterstützung für Bildbeschreibungen ist noch nicht vorhanden, wird jedoch in einer künftigen Version nachgereicht.
Bilder, die in einzelne Sprachversionen der Wikipedia hochgeladen wurden, stehen nicht zur Verfügung. Dies ist eine bewusste Einschränkung und es ist nicht geplant, dies zu ändern. Verfügbar sind lediglich Bilder von Wikimedia Commons, da dies der zentrale Ort für die gemeinsame Nutzung von Bildern über alle Wikipedia-Sprachversionen hinweg ist. Ebenso stehen keine Bilder von Websites Dritter zur Verfügung.
Solltest du auf Probleme stoßen, lass es uns bitte wissen. Angesichts des komplexen Ökosystems, in dem wir uns bewegen, rechnen wir, wie bei jeder neuen Funktion, damit, dass manches möglicherweise nicht wie vorgesehen funktioniert. Bitte melde solche Fälle, idealerweise über Phabricator, damit wir sie beheben können.
Ein erstes Bild, das in einen Artikel in der Abstrakten Wikipedia eingebunden ist, ist hier zu sehen: [[abstract:Q922|Brač]]. Wir freuen uns darauf, zu sehen, wir du Bilder in der Abstrakten verwendest!
<span id="Recent_Changes_in_the_software"></span>
=== Letzte Änderungen an der Software ===
Diese Woche haben wir die Art und Weise, wie der Code registriert wird, überarbeitet. Das bedeutet, dass spezielle Benutzerrechte (wie "Einen Test mit seiner Funktion verbinden" oder "Neue abstrakte Artikel erstellen") und Gruppen (wie "Funktionsbearbeiter") nun nur noch dort angezeigt werden, wo sie relevant sind, also auf Wikifunctions oder in der Abstrakten Wikipedia, nicht jedoch in Client-Wikis ([[:phab:T407066|T407066]]). Zudem haben wir einen Fehler auf Wikifunctions behoben, durch den auf Versionsunterschiedsseiten unterhalb des Vergleichs die alte statt der neuen Version einer Seite angezeigt wurde. Die Abstrakte Wikipedia war davon nicht betroffen.
<span id="Volunteers’_Corner_on_June_8"></span>
=== Freiwilligentreffen am 8. Juni ===
Das nächste Freiwilligentreffen findet am [https://zonestamp.toolforge.org/1780939800 Montag, dem 8. Juni 2026, um 19:30 Uhr MESZ] statt. Wir planen, auf Fragen aus der Community einzugehen und alle aufkommenden Themen zu besprechen. Sofern die Zeit reicht, werden wir gemeinsam eine Funktion erstellen. Jeder ist herzlich eingeladen, über [https://meet.google.com/xuy-njxh-rkw Google Meet] teilzunehmen.
<span id="Functions_of_the_Week:_{{Z|Z36083}}_and_{{Z|Z33842}}"></span>
=== Funktionen der Woche: {{Z|Z36083}} und {{Z|Z33842}} ===
: ''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[Wikifunctions:Function of the Week/submissions|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[User:99of9|99of9]] erstellt und von [[User:Feeglgeef|Feeglgeef]] redaktionell bearbeitet. Danke!''
Die Funktionen [[Z36083|Hauptartikel]] und [[Z33842|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[w:Template:Main|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
Die Vollversion unserer Funktion [[Z33842|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[Z6091|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[Z89|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[Z33847|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut <code>role="note" class="hatnote navigation-not-searchable"</code>, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[Z36083|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[:abstract:Q408|Australien (Q408)]] basieren:
* [[Z36085|''Main article:'' History of Australia]]
* [[Z36086|''Main articles:'' Geography of Australia and Australian continent]]
* [[Z36087|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, [[phab:T427454|T427454]], fehl, der [[Z13464|Z13464]] betrifft, worauf sich die Komposition verlässt.
* [[Z36089|''Hoofdartikel:'' Geografie van Australië]] in Belgischem Niederländisch.
* [[Z36088|''→ Hauptartikel:'' Klima in Australien]] wartet auf eine Implementierung in Deutsch.
* [[Z36090|ein Test der Standardfunktion]], der in Arabisch testet, einer von rechts nach links geschriebenen Sprache, und erfolgreich <code><span dir="rtl">← تاريخ أستراليا</span></code> mit einem Link generiert.
Ich habe dieses Beispiel als Funktion der Woche ausgewählt, um zu weiterer Sprachkonfiguration zu ermutigen. Die Unterstützung einfacher Links mit Bezeichnungen nach einem einheitlichen Schema ist ein sehr klar definierter Anwendungszweck und stellt im Vergleich zu anderen Funktionen der natürlichen Sprache einen relativ einfachen Einstiegspunkt dar. Derzeit ist die Funktion nur für Englisch und Niederländisch [[Z33855|konfiguriert]], daher wären weitere Beiträge willkommen. Zudem wäre es interessant, theoretische Ansätze für das aktuell ungenutzte Argument zur Konfiguration von Bezeichnungen zu diskutieren.
<span id="Fresh_Functions_weekly:_63_new_Functions"></span>
=== Wöchentliche neue Funktionen: 63 neue Funktionen ===
Diese Woche hatten wir 63 neue Funktionen. Hier ist eine unvollständige Liste von Funktionen mit Implementierungen und bestandenen Tests, um einen Eindruck davon zu bekommen, welche Funktionen erstellt wurden. Vielen Dank an alle für ihre Beiträge!
* {{Z|Z35641}}
* {{Z|Z35646}}
* {{Z|Z35652}}
* {{Z|Z35656}}
* {{Z|Z35663}}
* {{Z|Z35666}}
* {{Z|Z35672}}
* {{Z|Z35677}}
* {{Z|Z35683}}
* {{Z|Z35687}}
* {{Z|Z35693}}
* {{Z|Z35702}}
* {{Z|Z35709}}
* {{Z|Z35714}}
* {{Z|Z35721}}
* {{Z|Z35727}}
* {{Z|Z35732}}
* {{Z|Z35737}}
* {{Z|Z35740}}
* {{Z|Z35747}}
* {{Z|Z35766}}
* {{Z|Z35772}}
* {{Z|Z35774}}
* {{Z|Z35776}}
* {{Z|Z35780}}
* {{Z|Z35792}}
* {{Z|Z35797}}
* {{Z|Z35806}}
* {{Z|Z35809}}
* {{Z|Z35811}}
* {{Z|Z35815}}
* {{Z|Z35828}}
* {{Z|Z35839}}
* {{Z|Z35847}}
* {{Z|Z35860}}
* {{Z|Z35864}}
* {{Z|Z35874}}
* {{Z|Z35879}}
* {{Z|Z35883}}
* {{Z|Z35889}}
* {{Z|Z35892}}
* {{Z|Z35901}}
* {{Z|Z35911}}
* {{Z|Z35921}}
* {{Z|Z35936}}
* {{Z|Z35941}}
Eine [https://www.wikifunctions.org/wiki/Special:ListObjectsByType?type=Z8&orderby=latest vollständige Liste aller Funktionen, sortiert nach ihrem Erstellungszeitpunkt], ist verfügbar.
[[Category:Status updates{{#translation:}}|2026-06-05]]
8aqy6tcv49ln8e00l1on8tlt85u3r8d
User talk:Seller of unexistent friends
3
85229
281633
281130
2026-06-07T21:31:39Z
Feeglgeef
8776
/* Connection? */ new section
281633
wikitext
text/x-wiki
{{Welcome/lang|user=Seller of unexistent friends|welcominguser=Feeglgeef|1=[[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 01:43, 7 June 2026 (UTC)}}
== Connection? ==
Hi! Would you like me to connect the tests and implementation of [[Z10139]]? That would mean that the function can be used, but you would no longer be able to edit it without a [[WF:Functioneer|functioneer]]. In the future, you can also request that a functioneer perform a connection or disconnection for you at [[WF:RFCD]]. Thanks, [[User:Feeglgeef|Feeglgeef]] ([[User talk:Feeglgeef|talk]]) 21:31, 7 June 2026 (UTC)
5blypsaxxiyxq6ett7wzj3n9hnyn5xx
Z36166
0
85247
281211
281203
2026-06-07T12:32:27Z
HenkvD
1290
[mk] A е B од C.
281211
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36166"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z6091",
"Z17K2": "Z36166K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "entity"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z6091",
"Z17K2": "Z36166K2",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "class"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z6091",
"Z17K2": "Z36166K3",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "creator"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z60",
"Z17K2": "Z36166K4",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "language"
}
]
}
}
],
"Z8K2": "Z11",
"Z8K3": [
"Z20",
"Z36167"
],
"Z8K4": [
"Z14",
"Z36168"
],
"Z8K5": "Z36166"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Creative work - entity, class, creator, Simple"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1157",
"Z11K2": "[nl] A is een B van C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1216",
"Z11K2": "[fy] A is in B fan C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1532",
"Z11K2": "[af] A is 'n B deur C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1576",
"Z11K2": "[eo] A estas B de C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1137",
"Z11K2": "[pap] A ta un B di C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1106",
"Z11K2": "[is] A er B eftir C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1402",
"Z11K2": "[mk] A е B од C."
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
8lthmnb5l7bz9b4ls5k0pca24leieq9
281212
281211
2026-06-07T12:33:01Z
HenkvD
1290
[os] A у B да C.
281212
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36166"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z6091",
"Z17K2": "Z36166K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "entity"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z6091",
"Z17K2": "Z36166K2",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "class"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z6091",
"Z17K2": "Z36166K3",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "creator"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z60",
"Z17K2": "Z36166K4",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "language"
}
]
}
}
],
"Z8K2": "Z11",
"Z8K3": [
"Z20",
"Z36167"
],
"Z8K4": [
"Z14",
"Z36168"
],
"Z8K5": "Z36166"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Creative work - entity, class, creator, Simple"
},
{
"Z1K1": "Z11",
"Z11K1": "Z1157",
"Z11K2": "[nl] A is een B van C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1216",
"Z11K2": "[fy] A is in B fan C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1532",
"Z11K2": "[af] A is 'n B deur C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1576",
"Z11K2": "[eo] A estas B de C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1137",
"Z11K2": "[pap] A ta un B di C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1106",
"Z11K2": "[is] A er B eftir C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1402",
"Z11K2": "[mk] A е B од C."
},
{
"Z1K1": "Z11",
"Z11K1": "Z1798",
"Z11K2": "[os] A у B да C."
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
g8bmxx07incacmruk3gq7odtq53ded2
Z36168
0
85249
281210
281194
2026-06-07T12:28:45Z
HenkvD
1290
+mk +os
281210
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36168"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z36166",
"Z14K2": {
"Z1K1": "Z7",
"Z7K1": "Z26107",
"Z26107K1": {
"Z1K1": "Z18",
"Z18K1": "Z36166K4"
},
"Z26107K2": {
"Z1K1": "Z7",
"Z7K1": "Z21394",
"Z21394K1": [
"Z6",
{
"Z1K1": "Z7",
"Z7K1": "Z24766",
"Z24766K1": {
"Z1K1": "Z18",
"Z18K1": "Z36166K1"
},
"Z24766K2": {
"Z1K1": "Z18",
"Z18K1": "Z36166K4"
}
},
{
"Z1K1": "Z7",
"Z7K1": "Z22193",
"Z22193K1": {
"Z1K1": "Z18",
"Z18K1": "Z36166K4"
},
"Z22193K2": [
"Z60",
"Z1216",
"Z1532",
"Z1730",
"Z1473",
"Z1576",
"Z1650",
"Z1106",
"Z1642",
"Z1402",
"Z1146",
"Z1798",
"Z1137",
"Z1158",
"Z1157"
],
"Z22193K3": [
"Z6",
" is in ",
" is 'n ",
" is a ",
" je ",
" estas ",
" as en ",
" er ",
" je ",
" е ",
" is en ",
" у ",
" ta un ",
" је ",
" is een ",
" (is a) "
]
},
{
"Z1K1": "Z7",
"Z7K1": "Z24766",
"Z24766K1": {
"Z1K1": "Z18",
"Z18K1": "Z36166K2"
},
"Z24766K2": {
"Z1K1": "Z18",
"Z18K1": "Z36166K4"
}
},
{
"Z1K1": "Z7",
"Z7K1": "Z22193",
"Z22193K1": {
"Z1K1": "Z18",
"Z18K1": "Z36166K4"
},
"Z22193K2": [
"Z60",
"Z1157",
"Z1216",
"Z1532",
"Z1576",
"Z1137",
"Z1106",
"Z1402",
"Z1798"
],
"Z22193K3": [
"Z6",
" van ",
" fan ",
" deur ",
" de ",
" di ",
" eftir ",
" од ",
" да ",
" (by) "
]
},
{
"Z1K1": "Z7",
"Z7K1": "Z24766",
"Z24766K1": {
"Z1K1": "Z18",
"Z18K1": "Z36166K3"
},
"Z24766K2": {
"Z1K1": "Z18",
"Z18K1": "Z36166K4"
}
},
"."
]
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Creative work - entity, class, creator, Simple com"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
3tfoiqcx1qmb3mkwbbr2spth9cv6egx
Talk:Z36166
1
85252
281213
281204
2026-06-07T12:34:45Z
HenkvD
1290
281213
wikitext
text/x-wiki
== Simple rules for A is a B by C for many languages ==
This function and implementation is for many languages with simple rules for "A is a B by C".
:[af] A is 'n B deur C.
:[eo] A estas B de C.
:[fy] A is in B fan C.
:[is] A er B eftir C.
:[mk] A е B од C.
:[nl] A is een B van C.
:[os] A у B да C.
:[pap] A ta un B di C.
[[User:HenkvD|HenkvD]] ([[User talk:HenkvD|talk]]) 12:34, 7 June 2026 (UTC)
i761pctssutmkk0eb4xoq0g377opj7c
Z36171
0
85253
281205
2026-06-07T12:08:13Z
Seller of unexistent friends
85698
Add a python implementation that uses hashlib
281205
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "def Z10139(Z10139K1):\n\timport hashlib\n\th = hashlib.new('ripemd160')\n h.update(Z10139K1.encode())\n return h.hexdigest()"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Python via hashlib"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
hzevh14ii3p9gg207uq95nwswmw18xd
281206
281205
2026-06-07T12:10:20Z
Seller of unexistent friends
85698
281206
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "def Z10139(Z10139K1: str):\n import hashlib\n\n h = hashlib.new(\"ripemd160\")\n h.update(Z10139K1.encode())\n return h.hexdigest()"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Python via hashlib"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
2pr4dy948ul8f1o213sgqz900ttdxp7
281216
281206
2026-06-07T14:40:28Z
Seller of unexistent friends
85698
Fix the implementation by making it not rely on hashlib.
281216
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "# Python translation of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.c\n# https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.h\n# and a bit of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/hashtest.c\n# It's not really pythonic but it's fine since it works.\n\nfrom ctypes import c_uint8, c_uint32\n\ntype u8 = c_uint8\ntype u32 = c_uint32\n\n\n# ROL(x, n) cyclically rotates x over n bits to the left\n# x must be of an unsigned 32 bits type and 0 \u003C= n \u003C 32.\ndef ROL(x: u32, n: u32):\n return c_uint32(((x.value) \u003C\u003C (n.value)) | ((x.value) \u003E\u003E (32 - (n.value))))\n\n\n##### The five basic functions F(), G() and H() #####\ndef F(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ (y.value) ^ (z.value))\n\n\ndef G(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (y.value)) | (~(x.value) \u0026 (z.value)))\n\n\ndef H(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) | ~(y.value)) ^ (z.value))\n\n\ndef I(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (z.value)) | ((y.value) \u0026 ~(z.value)))\n\n\ndef J(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ ((y.value) | ~(z.value)))\n\n\n##### The ten basic operations FF() through III() #####\ndef FF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x5A827999).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6ED9EBA1).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef II(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x8F1BBCDC).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0xA953FD4E).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef FFF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GGG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x7A6D76E9).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HHH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6D703EF3).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef III(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x5C4DD124).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0x50A28BE6).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\n# Initializes MDbuffer to \"magic constants\"\ndef MDinit(MDbuf: list[u32]):\n MDbuf[0] = c_uint32(0x67452301)\n MDbuf[1] = c_uint32(0xEFCDAB89)\n MDbuf[2] = c_uint32(0x98BADCFE)\n MDbuf[3] = c_uint32(0x10325476)\n MDbuf[4] = c_uint32(0xC3D2E1F0)\n\n\n##### The compression function. Transforms MDbuf using message bytes X[0] through X[15] #####\ndef compress(MDbuf: list[u32], X: list[u32]):\n aa = c_uint32(MDbuf[0].value)\n bb = c_uint32(MDbuf[1].value)\n cc = c_uint32(MDbuf[2].value)\n dd = c_uint32(MDbuf[3].value)\n ee = c_uint32(MDbuf[4].value)\n\n aaa = c_uint32(MDbuf[0].value)\n bbb = c_uint32(MDbuf[1].value)\n ccc = c_uint32(MDbuf[2].value)\n ddd = c_uint32(MDbuf[3].value)\n eee = c_uint32(MDbuf[4].value)\n\n # round 1\n FF(aa, bb, cc, dd, ee, X[0], c_uint32(11))\n FF(ee, aa, bb, cc, dd, X[1], c_uint32(14))\n FF(dd, ee, aa, bb, cc, X[2], c_uint32(15))\n FF(cc, dd, ee, aa, bb, X[3], c_uint32(12))\n FF(bb, cc, dd, ee, aa, X[4], c_uint32(5))\n FF(aa, bb, cc, dd, ee, X[5], c_uint32(8))\n FF(ee, aa, bb, cc, dd, X[6], c_uint32(7))\n FF(dd, ee, aa, bb, cc, X[7], c_uint32(9))\n FF(cc, dd, ee, aa, bb, X[8], c_uint32(11))\n FF(bb, cc, dd, ee, aa, X[9], c_uint32(13))\n FF(aa, bb, cc, dd, ee, X[10], c_uint32(14))\n FF(ee, aa, bb, cc, dd, X[11], c_uint32(15))\n FF(dd, ee, aa, bb, cc, X[12], c_uint32(6))\n FF(cc, dd, ee, aa, bb, X[13], c_uint32(7))\n FF(bb, cc, dd, ee, aa, X[14], c_uint32(9))\n FF(aa, bb, cc, dd, ee, X[15], c_uint32(8))\n\n # round 2\n GG(ee, aa, bb, cc, dd, X[7], c_uint32(7))\n GG(dd, ee, aa, bb, cc, X[4], c_uint32(6))\n GG(cc, dd, ee, aa, bb, X[13], c_uint32(8))\n GG(bb, cc, dd, ee, aa, X[1], c_uint32(13))\n GG(aa, bb, cc, dd, ee, X[10], c_uint32(11))\n GG(ee, aa, bb, cc, dd, X[6], c_uint32(9))\n GG(dd, ee, aa, bb, cc, X[15], c_uint32(7))\n GG(cc, dd, ee, aa, bb, X[3], c_uint32(15))\n GG(bb, cc, dd, ee, aa, X[12], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[0], c_uint32(12))\n GG(ee, aa, bb, cc, dd, X[9], c_uint32(15))\n GG(dd, ee, aa, bb, cc, X[5], c_uint32(9))\n GG(cc, dd, ee, aa, bb, X[2], c_uint32(11))\n GG(bb, cc, dd, ee, aa, X[14], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[11], c_uint32(13))\n GG(ee, aa, bb, cc, dd, X[8], c_uint32(12))\n\n # round 3\n HH(dd, ee, aa, bb, cc, X[3], c_uint32(11))\n HH(cc, dd, ee, aa, bb, X[10], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[14], c_uint32(6))\n HH(aa, bb, cc, dd, ee, X[4], c_uint32(7))\n HH(ee, aa, bb, cc, dd, X[9], c_uint32(14))\n HH(dd, ee, aa, bb, cc, X[15], c_uint32(9))\n HH(cc, dd, ee, aa, bb, X[8], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[1], c_uint32(15))\n HH(aa, bb, cc, dd, ee, X[2], c_uint32(14))\n HH(ee, aa, bb, cc, dd, X[7], c_uint32(8))\n HH(dd, ee, aa, bb, cc, X[0], c_uint32(13))\n HH(cc, dd, ee, aa, bb, X[6], c_uint32(6))\n HH(bb, cc, dd, ee, aa, X[13], c_uint32(5))\n HH(aa, bb, cc, dd, ee, X[11], c_uint32(12))\n HH(ee, aa, bb, cc, dd, X[5], c_uint32(7))\n HH(dd, ee, aa, bb, cc, X[12], c_uint32(5))\n\n # round 4\n II(cc, dd, ee, aa, bb, X[1], c_uint32(11))\n II(bb, cc, dd, ee, aa, X[9], c_uint32(12))\n II(aa, bb, cc, dd, ee, X[11], c_uint32(14))\n II(ee, aa, bb, cc, dd, X[10], c_uint32(15))\n II(dd, ee, aa, bb, cc, X[0], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[8], c_uint32(15))\n II(bb, cc, dd, ee, aa, X[12], c_uint32(9))\n II(aa, bb, cc, dd, ee, X[4], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[13], c_uint32(9))\n II(dd, ee, aa, bb, cc, X[3], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[7], c_uint32(5))\n II(bb, cc, dd, ee, aa, X[15], c_uint32(6))\n II(aa, bb, cc, dd, ee, X[14], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[5], c_uint32(6))\n II(dd, ee, aa, bb, cc, X[6], c_uint32(5))\n II(cc, dd, ee, aa, bb, X[2], c_uint32(12))\n\n # round 5\n JJ(bb, cc, dd, ee, aa, X[4], c_uint32(9))\n JJ(aa, bb, cc, dd, ee, X[0], c_uint32(15))\n JJ(ee, aa, bb, cc, dd, X[5], c_uint32(5))\n JJ(dd, ee, aa, bb, cc, X[9], c_uint32(11))\n JJ(cc, dd, ee, aa, bb, X[7], c_uint32(6))\n JJ(bb, cc, dd, ee, aa, X[12], c_uint32(8))\n JJ(aa, bb, cc, dd, ee, X[2], c_uint32(13))\n JJ(ee, aa, bb, cc, dd, X[10], c_uint32(12))\n JJ(dd, ee, aa, bb, cc, X[14], c_uint32(5))\n JJ(cc, dd, ee, aa, bb, X[1], c_uint32(12))\n JJ(bb, cc, dd, ee, aa, X[3], c_uint32(13))\n JJ(aa, bb, cc, dd, ee, X[8], c_uint32(14))\n JJ(ee, aa, bb, cc, dd, X[11], c_uint32(11))\n JJ(dd, ee, aa, bb, cc, X[6], c_uint32(8))\n JJ(cc, dd, ee, aa, bb, X[15], c_uint32(5))\n JJ(bb, cc, dd, ee, aa, X[13], c_uint32(6))\n\n # parallel round 1\n JJJ(aaa, bbb, ccc, ddd, eee, X[5], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[14], c_uint32(9))\n JJJ(ddd, eee, aaa, bbb, ccc, X[7], c_uint32(9))\n JJJ(ccc, ddd, eee, aaa, bbb, X[0], c_uint32(11))\n JJJ(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(13))\n JJJ(aaa, bbb, ccc, ddd, eee, X[2], c_uint32(15))\n JJJ(eee, aaa, bbb, ccc, ddd, X[11], c_uint32(15))\n JJJ(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(5))\n JJJ(ccc, ddd, eee, aaa, bbb, X[13], c_uint32(7))\n JJJ(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(7))\n JJJ(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(11))\n JJJ(ddd, eee, aaa, bbb, ccc, X[1], c_uint32(14))\n JJJ(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(14))\n JJJ(bbb, ccc, ddd, eee, aaa, X[3], c_uint32(12))\n JJJ(aaa, bbb, ccc, ddd, eee, X[12], c_uint32(6))\n\n # parallel round 2\n III(eee, aaa, bbb, ccc, ddd, X[6], c_uint32(9))\n III(ddd, eee, aaa, bbb, ccc, X[11], c_uint32(13))\n III(ccc, ddd, eee, aaa, bbb, X[3], c_uint32(15))\n III(bbb, ccc, ddd, eee, aaa, X[7], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(12))\n III(eee, aaa, bbb, ccc, ddd, X[13], c_uint32(8))\n III(ddd, eee, aaa, bbb, ccc, X[5], c_uint32(9))\n III(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(11))\n III(bbb, ccc, ddd, eee, aaa, X[14], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(7))\n III(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(12))\n III(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(7))\n III(ccc, ddd, eee, aaa, bbb, X[4], c_uint32(6))\n III(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(15))\n III(aaa, bbb, ccc, ddd, eee, X[1], c_uint32(13))\n III(eee, aaa, bbb, ccc, ddd, X[2], c_uint32(11))\n\n # parallel round 3\n HHH(ddd, eee, aaa, bbb, ccc, X[15], c_uint32(9))\n HHH(ccc, ddd, eee, aaa, bbb, X[5], c_uint32(7))\n HHH(bbb, ccc, ddd, eee, aaa, X[1], c_uint32(15))\n HHH(aaa, bbb, ccc, ddd, eee, X[3], c_uint32(11))\n HHH(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(8))\n HHH(ddd, eee, aaa, bbb, ccc, X[14], c_uint32(6))\n HHH(ccc, ddd, eee, aaa, bbb, X[6], c_uint32(6))\n HHH(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(14))\n HHH(aaa, bbb, ccc, ddd, eee, X[11], c_uint32(12))\n HHH(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(13))\n HHH(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(5))\n HHH(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(14))\n HHH(bbb, ccc, ddd, eee, aaa, X[10], c_uint32(13))\n HHH(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(13))\n HHH(eee, aaa, bbb, ccc, ddd, X[4], c_uint32(7))\n HHH(ddd, eee, aaa, bbb, ccc, X[13], c_uint32(5))\n\n # parallel round 4\n GGG(ccc, ddd, eee, aaa, bbb, X[8], c_uint32(15))\n GGG(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(5))\n GGG(aaa, bbb, ccc, ddd, eee, X[4], c_uint32(8))\n GGG(eee, aaa, bbb, ccc, ddd, X[1], c_uint32(11))\n GGG(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(14))\n GGG(ccc, ddd, eee, aaa, bbb, X[11], c_uint32(14))\n GGG(bbb, ccc, ddd, eee, aaa, X[15], c_uint32(6))\n GGG(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(14))\n GGG(eee, aaa, bbb, ccc, ddd, X[5], c_uint32(6))\n GGG(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(9))\n GGG(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(12))\n GGG(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(9))\n GGG(aaa, bbb, ccc, ddd, eee, X[9], c_uint32(12))\n GGG(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(5))\n GGG(ddd, eee, aaa, bbb, ccc, X[10], c_uint32(15))\n GGG(ccc, ddd, eee, aaa, bbb, X[14], c_uint32(8))\n\n # parallel round 5\n FFF(bbb, ccc, ddd, eee, aaa, X[12], c_uint32(8))\n FFF(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[10], c_uint32(12))\n FFF(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(9))\n FFF(ccc, ddd, eee, aaa, bbb, X[1], c_uint32(12))\n FFF(bbb, ccc, ddd, eee, aaa, X[5], c_uint32(5))\n FFF(aaa, bbb, ccc, ddd, eee, X[8], c_uint32(14))\n FFF(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(6))\n FFF(ddd, eee, aaa, bbb, ccc, X[6], c_uint32(8))\n FFF(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(13))\n FFF(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(6))\n FFF(aaa, bbb, ccc, ddd, eee, X[14], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[0], c_uint32(15))\n FFF(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(13))\n FFF(ccc, ddd, eee, aaa, bbb, X[9], c_uint32(11))\n FFF(bbb, ccc, ddd, eee, aaa, X[11], c_uint32(11))\n\n # combine results\n ddd.value += cc.value + MDbuf[1].value # final result for MDbuf[0]\n MDbuf[1].value = MDbuf[2].value + dd.value + eee.value\n MDbuf[2].value = MDbuf[3].value + ee.value + aaa.value\n MDbuf[3].value = MDbuf[4].value + aa.value + bbb.value\n MDbuf[4].value = MDbuf[0].value + bb.value + ccc.value\n MDbuf[0].value = ddd.value\n\n\n##### Puts bytes from strptr into X and pad out; appends length\n##### and finally, compresses the last block(s)\n##### note: length in bits == 8 * (lswlen + 2^32 mswlen).\n##### note: there are (lswlen mod 64) bytes left in strptr.\n\n\ndef MDfinish(MDbuf: list[u32], bytes: list[u8], lswlen: u32, mswlen: u32):\n i = c_uint32() # counter\n x = [c_uint32(0) for _ in range(16)]\n str_idx = 0\n\n # /* put bytes from strptr into X\n while i.value \u003C (lswlen.value \u0026 63):\n # byte i goes into word X[i div 4] at pos. 8*(i mod 4)\n x[i.value \u003E\u003E 2].value ^= (\n c_uint32(bytes[str_idx].value).value \u003C\u003C c_uint32((8 * (i.value \u0026 3))).value\n )\n i.value += 1\n str_idx += 1\n\n # append the bit m_n == 1\n x[(lswlen.value \u003E\u003E c_uint32(2).value) \u0026 15].value ^= c_uint32(1).value \u003C\u003C (\n 8 * (lswlen.value \u0026 3) + 7\n )\n\n if (lswlen.value \u0026 c_uint32(63).value) \u003E 55:\n compress(MDbuf, x)\n x = [c_uint32(0) for _ in range(16)]\n\n # append length in bits\n x[14].value = lswlen.value \u003C\u003C c_uint32(3).value\n x[15].value = (lswlen.value \u003E\u003E c_uint32(29).value) | (\n mswlen.value \u003C\u003C c_uint32(3).value\n )\n compress(MDbuf, x)\n\n\nRMDsize = 160\n\n\n# Converts 4 u8s into 1 u32\ndef u8_to_u32(strptr: list[u8]):\n return c_uint32(\n ((strptr[3]).value \u003C\u003C 24)\n | ((strptr[2]).value \u003C\u003C 16)\n | ((strptr[1]).value \u003C\u003C 8)\n | ((strptr[0].value))\n )\n\n\n##### Returns the RIPEMD-160 digest of the message in bytes #####\ndef RMD(message: list[u8]):\n MDbuf = [\n c_uint32(0) for _ in range((RMDsize / 32).__floor__())\n ] # contains (A, B, C, D, E)\n hashcode = [c_uint8(0) for _ in range((RMDsize / 8).__floor__())]\n # for final hash-value\n x = [c_uint32(0) for _ in range(16)] # current 16-word chunk\n i = c_uint32(0) # counter\n length = message.__len__() # length in bytes of message\n nbytes = message.__len__() # number of bytes not yet processed\n message_idx = 0\n\n MDinit(MDbuf)\n\n # process message in 16-word chunks\n\n while nbytes \u003E 63:\n i.value = 0\n while i.value \u003C 16:\n x[i.value] = u8_to_u32(message[message_idx:])\n message_idx += 4\n i.value += 1\n compress(MDbuf, x)\n nbytes -= 64\n\n # finish:\n MDfinish(MDbuf, message, c_uint32(length), c_uint32(0))\n i.value = 0\n while i.value \u003C RMDsize / 8:\n hashcode[i.value] = c_uint8(MDbuf[i.value \u003E\u003E 2].value)\n hashcode[i.value + 1] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 8)\n hashcode[i.value + 2] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 16)\n hashcode[i.value + 3] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 24)\n i.value += 4\n\n return hashcode\n\n\n##### Returns the RIPEMD-160 hash of the message in a string #####\ndef RMDstring(message: str):\n i = 0\n encoded_message: list[c_uint8] = [c_uint8(x) for x in list(message.encode())]\n\n hashcode = RMD(encoded_message)\n\n string = \"\"\n for i in hashcode:\n string += f\"{i:x}\"\n return string\n\ndef Z10139(Z10139K1: str):\n return RMDstring(Z10139K1)\n"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Pure Python"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
383j7z63jmldvncxpnoi41pmsfs514l
281217
281216
2026-06-07T15:37:04Z
Seller of unexistent friends
85698
Fix the implementation so that it doesn't rely on ctypes anymore.
281217
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "# Python translation of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.c\n# https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.h\n# and a bit of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/hashtest.c\n# It's not really pythonic but it's fine since it works.\n\nfrom typing import override\n\n\nclass _FixedUint:\n _mask: int = 0\n\n def __init__(self, value: int = 0):\n self._value: int = value \u0026 self._mask\n\n @property\n def value(self) -\u003E int:\n return self._value\n\n @value.setter\n def value(self, v: int):\n self._value = v \u0026 self._mask\n\n @override\n def __format__(self, spec: str) -\u003E str:\n return format(self._value, spec)\n\n @override\n def __repr__(self) -\u003E str:\n return f\"{type(self).__name__}({self._value})\"\n\n\nclass c_uint8(_FixedUint):\n _mask: int = 0xFF\n\n\nclass c_uint32(_FixedUint):\n _mask: int = 0xFFFFFFFF\n\n\ntype u8 = c_uint8\ntype u32 = c_uint32\n\n\n# ROL(x, n) cyclically rotates x over n bits to the left\n# x must be of an unsigned 32 bits type and 0 \u003C= n \u003C 32.\ndef ROL(x: u32, n: u32):\n return c_uint32(((x.value) \u003C\u003C (n.value)) | ((x.value) \u003E\u003E (32 - (n.value))))\n\n\n##### The five basic functions F(), G() and H() #####\ndef F(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ (y.value) ^ (z.value))\n\n\ndef G(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (y.value)) | (~(x.value) \u0026 (z.value)))\n\n\ndef H(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) | ~(y.value)) ^ (z.value))\n\n\ndef I(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (z.value)) | ((y.value) \u0026 ~(z.value)))\n\n\ndef J(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ ((y.value) | ~(z.value)))\n\n\n##### The ten basic operations FF() through III() #####\ndef FF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x5A827999).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6ED9EBA1).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef II(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x8F1BBCDC).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0xA953FD4E).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef FFF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GGG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x7A6D76E9).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HHH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6D703EF3).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef III(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x5C4DD124).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0x50A28BE6).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\n# Initializes MDbuffer to \"magic constants\"\ndef MDinit(MDbuf: list[u32]):\n MDbuf[0] = c_uint32(0x67452301)\n MDbuf[1] = c_uint32(0xEFCDAB89)\n MDbuf[2] = c_uint32(0x98BADCFE)\n MDbuf[3] = c_uint32(0x10325476)\n MDbuf[4] = c_uint32(0xC3D2E1F0)\n\n\n##### The compression function. Transforms MDbuf using message bytes X[0] through X[15] #####\ndef compress(MDbuf: list[u32], X: list[u32]):\n aa = c_uint32(MDbuf[0].value)\n bb = c_uint32(MDbuf[1].value)\n cc = c_uint32(MDbuf[2].value)\n dd = c_uint32(MDbuf[3].value)\n ee = c_uint32(MDbuf[4].value)\n\n aaa = c_uint32(MDbuf[0].value)\n bbb = c_uint32(MDbuf[1].value)\n ccc = c_uint32(MDbuf[2].value)\n ddd = c_uint32(MDbuf[3].value)\n eee = c_uint32(MDbuf[4].value)\n\n # round 1\n FF(aa, bb, cc, dd, ee, X[0], c_uint32(11))\n FF(ee, aa, bb, cc, dd, X[1], c_uint32(14))\n FF(dd, ee, aa, bb, cc, X[2], c_uint32(15))\n FF(cc, dd, ee, aa, bb, X[3], c_uint32(12))\n FF(bb, cc, dd, ee, aa, X[4], c_uint32(5))\n FF(aa, bb, cc, dd, ee, X[5], c_uint32(8))\n FF(ee, aa, bb, cc, dd, X[6], c_uint32(7))\n FF(dd, ee, aa, bb, cc, X[7], c_uint32(9))\n FF(cc, dd, ee, aa, bb, X[8], c_uint32(11))\n FF(bb, cc, dd, ee, aa, X[9], c_uint32(13))\n FF(aa, bb, cc, dd, ee, X[10], c_uint32(14))\n FF(ee, aa, bb, cc, dd, X[11], c_uint32(15))\n FF(dd, ee, aa, bb, cc, X[12], c_uint32(6))\n FF(cc, dd, ee, aa, bb, X[13], c_uint32(7))\n FF(bb, cc, dd, ee, aa, X[14], c_uint32(9))\n FF(aa, bb, cc, dd, ee, X[15], c_uint32(8))\n\n # round 2\n GG(ee, aa, bb, cc, dd, X[7], c_uint32(7))\n GG(dd, ee, aa, bb, cc, X[4], c_uint32(6))\n GG(cc, dd, ee, aa, bb, X[13], c_uint32(8))\n GG(bb, cc, dd, ee, aa, X[1], c_uint32(13))\n GG(aa, bb, cc, dd, ee, X[10], c_uint32(11))\n GG(ee, aa, bb, cc, dd, X[6], c_uint32(9))\n GG(dd, ee, aa, bb, cc, X[15], c_uint32(7))\n GG(cc, dd, ee, aa, bb, X[3], c_uint32(15))\n GG(bb, cc, dd, ee, aa, X[12], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[0], c_uint32(12))\n GG(ee, aa, bb, cc, dd, X[9], c_uint32(15))\n GG(dd, ee, aa, bb, cc, X[5], c_uint32(9))\n GG(cc, dd, ee, aa, bb, X[2], c_uint32(11))\n GG(bb, cc, dd, ee, aa, X[14], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[11], c_uint32(13))\n GG(ee, aa, bb, cc, dd, X[8], c_uint32(12))\n\n # round 3\n HH(dd, ee, aa, bb, cc, X[3], c_uint32(11))\n HH(cc, dd, ee, aa, bb, X[10], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[14], c_uint32(6))\n HH(aa, bb, cc, dd, ee, X[4], c_uint32(7))\n HH(ee, aa, bb, cc, dd, X[9], c_uint32(14))\n HH(dd, ee, aa, bb, cc, X[15], c_uint32(9))\n HH(cc, dd, ee, aa, bb, X[8], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[1], c_uint32(15))\n HH(aa, bb, cc, dd, ee, X[2], c_uint32(14))\n HH(ee, aa, bb, cc, dd, X[7], c_uint32(8))\n HH(dd, ee, aa, bb, cc, X[0], c_uint32(13))\n HH(cc, dd, ee, aa, bb, X[6], c_uint32(6))\n HH(bb, cc, dd, ee, aa, X[13], c_uint32(5))\n HH(aa, bb, cc, dd, ee, X[11], c_uint32(12))\n HH(ee, aa, bb, cc, dd, X[5], c_uint32(7))\n HH(dd, ee, aa, bb, cc, X[12], c_uint32(5))\n\n # round 4\n II(cc, dd, ee, aa, bb, X[1], c_uint32(11))\n II(bb, cc, dd, ee, aa, X[9], c_uint32(12))\n II(aa, bb, cc, dd, ee, X[11], c_uint32(14))\n II(ee, aa, bb, cc, dd, X[10], c_uint32(15))\n II(dd, ee, aa, bb, cc, X[0], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[8], c_uint32(15))\n II(bb, cc, dd, ee, aa, X[12], c_uint32(9))\n II(aa, bb, cc, dd, ee, X[4], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[13], c_uint32(9))\n II(dd, ee, aa, bb, cc, X[3], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[7], c_uint32(5))\n II(bb, cc, dd, ee, aa, X[15], c_uint32(6))\n II(aa, bb, cc, dd, ee, X[14], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[5], c_uint32(6))\n II(dd, ee, aa, bb, cc, X[6], c_uint32(5))\n II(cc, dd, ee, aa, bb, X[2], c_uint32(12))\n\n # round 5\n JJ(bb, cc, dd, ee, aa, X[4], c_uint32(9))\n JJ(aa, bb, cc, dd, ee, X[0], c_uint32(15))\n JJ(ee, aa, bb, cc, dd, X[5], c_uint32(5))\n JJ(dd, ee, aa, bb, cc, X[9], c_uint32(11))\n JJ(cc, dd, ee, aa, bb, X[7], c_uint32(6))\n JJ(bb, cc, dd, ee, aa, X[12], c_uint32(8))\n JJ(aa, bb, cc, dd, ee, X[2], c_uint32(13))\n JJ(ee, aa, bb, cc, dd, X[10], c_uint32(12))\n JJ(dd, ee, aa, bb, cc, X[14], c_uint32(5))\n JJ(cc, dd, ee, aa, bb, X[1], c_uint32(12))\n JJ(bb, cc, dd, ee, aa, X[3], c_uint32(13))\n JJ(aa, bb, cc, dd, ee, X[8], c_uint32(14))\n JJ(ee, aa, bb, cc, dd, X[11], c_uint32(11))\n JJ(dd, ee, aa, bb, cc, X[6], c_uint32(8))\n JJ(cc, dd, ee, aa, bb, X[15], c_uint32(5))\n JJ(bb, cc, dd, ee, aa, X[13], c_uint32(6))\n\n # parallel round 1\n JJJ(aaa, bbb, ccc, ddd, eee, X[5], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[14], c_uint32(9))\n JJJ(ddd, eee, aaa, bbb, ccc, X[7], c_uint32(9))\n JJJ(ccc, ddd, eee, aaa, bbb, X[0], c_uint32(11))\n JJJ(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(13))\n JJJ(aaa, bbb, ccc, ddd, eee, X[2], c_uint32(15))\n JJJ(eee, aaa, bbb, ccc, ddd, X[11], c_uint32(15))\n JJJ(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(5))\n JJJ(ccc, ddd, eee, aaa, bbb, X[13], c_uint32(7))\n JJJ(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(7))\n JJJ(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(11))\n JJJ(ddd, eee, aaa, bbb, ccc, X[1], c_uint32(14))\n JJJ(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(14))\n JJJ(bbb, ccc, ddd, eee, aaa, X[3], c_uint32(12))\n JJJ(aaa, bbb, ccc, ddd, eee, X[12], c_uint32(6))\n\n # parallel round 2\n III(eee, aaa, bbb, ccc, ddd, X[6], c_uint32(9))\n III(ddd, eee, aaa, bbb, ccc, X[11], c_uint32(13))\n III(ccc, ddd, eee, aaa, bbb, X[3], c_uint32(15))\n III(bbb, ccc, ddd, eee, aaa, X[7], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(12))\n III(eee, aaa, bbb, ccc, ddd, X[13], c_uint32(8))\n III(ddd, eee, aaa, bbb, ccc, X[5], c_uint32(9))\n III(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(11))\n III(bbb, ccc, ddd, eee, aaa, X[14], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(7))\n III(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(12))\n III(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(7))\n III(ccc, ddd, eee, aaa, bbb, X[4], c_uint32(6))\n III(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(15))\n III(aaa, bbb, ccc, ddd, eee, X[1], c_uint32(13))\n III(eee, aaa, bbb, ccc, ddd, X[2], c_uint32(11))\n\n # parallel round 3\n HHH(ddd, eee, aaa, bbb, ccc, X[15], c_uint32(9))\n HHH(ccc, ddd, eee, aaa, bbb, X[5], c_uint32(7))\n HHH(bbb, ccc, ddd, eee, aaa, X[1], c_uint32(15))\n HHH(aaa, bbb, ccc, ddd, eee, X[3], c_uint32(11))\n HHH(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(8))\n HHH(ddd, eee, aaa, bbb, ccc, X[14], c_uint32(6))\n HHH(ccc, ddd, eee, aaa, bbb, X[6], c_uint32(6))\n HHH(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(14))\n HHH(aaa, bbb, ccc, ddd, eee, X[11], c_uint32(12))\n HHH(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(13))\n HHH(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(5))\n HHH(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(14))\n HHH(bbb, ccc, ddd, eee, aaa, X[10], c_uint32(13))\n HHH(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(13))\n HHH(eee, aaa, bbb, ccc, ddd, X[4], c_uint32(7))\n HHH(ddd, eee, aaa, bbb, ccc, X[13], c_uint32(5))\n\n # parallel round 4\n GGG(ccc, ddd, eee, aaa, bbb, X[8], c_uint32(15))\n GGG(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(5))\n GGG(aaa, bbb, ccc, ddd, eee, X[4], c_uint32(8))\n GGG(eee, aaa, bbb, ccc, ddd, X[1], c_uint32(11))\n GGG(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(14))\n GGG(ccc, ddd, eee, aaa, bbb, X[11], c_uint32(14))\n GGG(bbb, ccc, ddd, eee, aaa, X[15], c_uint32(6))\n GGG(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(14))\n GGG(eee, aaa, bbb, ccc, ddd, X[5], c_uint32(6))\n GGG(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(9))\n GGG(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(12))\n GGG(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(9))\n GGG(aaa, bbb, ccc, ddd, eee, X[9], c_uint32(12))\n GGG(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(5))\n GGG(ddd, eee, aaa, bbb, ccc, X[10], c_uint32(15))\n GGG(ccc, ddd, eee, aaa, bbb, X[14], c_uint32(8))\n\n # parallel round 5\n FFF(bbb, ccc, ddd, eee, aaa, X[12], c_uint32(8))\n FFF(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[10], c_uint32(12))\n FFF(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(9))\n FFF(ccc, ddd, eee, aaa, bbb, X[1], c_uint32(12))\n FFF(bbb, ccc, ddd, eee, aaa, X[5], c_uint32(5))\n FFF(aaa, bbb, ccc, ddd, eee, X[8], c_uint32(14))\n FFF(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(6))\n FFF(ddd, eee, aaa, bbb, ccc, X[6], c_uint32(8))\n FFF(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(13))\n FFF(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(6))\n FFF(aaa, bbb, ccc, ddd, eee, X[14], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[0], c_uint32(15))\n FFF(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(13))\n FFF(ccc, ddd, eee, aaa, bbb, X[9], c_uint32(11))\n FFF(bbb, ccc, ddd, eee, aaa, X[11], c_uint32(11))\n\n # combine results\n ddd.value += cc.value + MDbuf[1].value # final result for MDbuf[0]\n MDbuf[1].value = MDbuf[2].value + dd.value + eee.value\n MDbuf[2].value = MDbuf[3].value + ee.value + aaa.value\n MDbuf[3].value = MDbuf[4].value + aa.value + bbb.value\n MDbuf[4].value = MDbuf[0].value + bb.value + ccc.value\n MDbuf[0].value = ddd.value\n\n\n##### Puts bytes from strptr into X and pad out; appends length\n##### and finally, compresses the last block(s)\n##### note: length in bits == 8 * (lswlen + 2^32 mswlen).\n##### note: there are (lswlen mod 64) bytes left in strptr.\n\n\ndef MDfinish(MDbuf: list[u32], bytes: list[u8], lswlen: u32, mswlen: u32):\n i = c_uint32() # counter\n x = [c_uint32(0) for _ in range(16)]\n str_idx = 0\n\n # /* put bytes from strptr into X\n while i.value \u003C (lswlen.value \u0026 63):\n # byte i goes into word X[i div 4] at pos. 8*(i mod 4)\n x[i.value \u003E\u003E 2].value ^= (\n c_uint32(bytes[str_idx].value).value \u003C\u003C c_uint32((8 * (i.value \u0026 3))).value\n )\n i.value += 1\n str_idx += 1\n\n # append the bit m_n == 1\n x[(lswlen.value \u003E\u003E c_uint32(2).value) \u0026 15].value ^= c_uint32(1).value \u003C\u003C (\n 8 * (lswlen.value \u0026 3) + 7\n )\n\n if (lswlen.value \u0026 c_uint32(63).value) \u003E 55:\n compress(MDbuf, x)\n x = [c_uint32(0) for _ in range(16)]\n\n # append length in bits\n x[14].value = lswlen.value \u003C\u003C c_uint32(3).value\n x[15].value = (lswlen.value \u003E\u003E c_uint32(29).value) | (\n mswlen.value \u003C\u003C c_uint32(3).value\n )\n compress(MDbuf, x)\n\n\nRMDsize = 160\n\n\n# Converts 4 u8s into 1 u32\ndef u8_to_u32(strptr: list[u8]):\n return c_uint32(\n ((strptr[3]).value \u003C\u003C 24)\n | ((strptr[2]).value \u003C\u003C 16)\n | ((strptr[1]).value \u003C\u003C 8)\n | ((strptr[0].value))\n )\n\n\n##### Returns the RIPEMD-160 digest of the message in bytes #####\ndef RMD(message: list[u8]):\n MDbuf = [\n c_uint32(0) for _ in range((RMDsize / 32).__floor__())\n ] # contains (A, B, C, D, E)\n hashcode = [c_uint8(0) for _ in range((RMDsize / 8).__floor__())]\n # for final hash-value\n x = [c_uint32(0) for _ in range(16)] # current 16-word chunk\n i = c_uint32(0) # counter\n length = message.__len__() # length in bytes of message\n nbytes = message.__len__() # number of bytes not yet processed\n message_idx = 0\n\n MDinit(MDbuf)\n\n # process message in 16-word chunks\n\n while nbytes \u003E 63:\n i.value = 0\n while i.value \u003C 16:\n x[i.value] = u8_to_u32(message[message_idx:])\n message_idx += 4\n i.value += 1\n compress(MDbuf, x)\n nbytes -= 64\n\n # finish:\n MDfinish(MDbuf, message, c_uint32(length), c_uint32(0))\n i.value = 0\n while i.value \u003C RMDsize / 8:\n hashcode[i.value] = c_uint8(MDbuf[i.value \u003E\u003E 2].value)\n hashcode[i.value + 1] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 8)\n hashcode[i.value + 2] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 16)\n hashcode[i.value + 3] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 24)\n i.value += 4\n\n return hashcode\n\n\n##### Returns the RIPEMD-160 hash of the message in a string #####\ndef RMDstring(message: str):\n i = 0\n encoded_message: list[c_uint8] = [c_uint8(x) for x in list(message.encode())]\n\n hashcode = RMD(encoded_message)\n\n string = \"\"\n for i in hashcode:\n string += f\"{i:02x}\"\n return string\n\n"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Pure Python"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
bl227awi11f8qcpgs16d3fy93wb9rxb
281218
281217
2026-06-07T15:38:05Z
Seller of unexistent friends
85698
281218
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "# Python translation of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.c\n# https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.h\n# and a bit of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/hashtest.c\n# It's not really pythonic but it's fine since it works.\n\n\nclass _FixedUint:\n _mask: int = 0\n\n def __init__(self, value: int = 0):\n self._value: int = value \u0026 self._mask\n\n @property\n def value(self) -\u003E int:\n return self._value\n\n @value.setter\n def value(self, v: int):\n self._value = v \u0026 self._mask\n\n def __format__(self, spec: str) -\u003E str:\n return format(self._value, spec)\n\n def __repr__(self) -\u003E str:\n return f\"{type(self).__name__}({self._value})\"\n\n\nclass c_uint8(_FixedUint):\n _mask: int = 0xFF\n\n\nclass c_uint32(_FixedUint):\n _mask: int = 0xFFFFFFFF\n\n\ntype u8 = c_uint8\ntype u32 = c_uint32\n\n\n# ROL(x, n) cyclically rotates x over n bits to the left\n# x must be of an unsigned 32 bits type and 0 \u003C= n \u003C 32.\ndef ROL(x: u32, n: u32):\n return c_uint32(((x.value) \u003C\u003C (n.value)) | ((x.value) \u003E\u003E (32 - (n.value))))\n\n\n##### The five basic functions F(), G() and H() #####\ndef F(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ (y.value) ^ (z.value))\n\n\ndef G(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (y.value)) | (~(x.value) \u0026 (z.value)))\n\n\ndef H(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) | ~(y.value)) ^ (z.value))\n\n\ndef I(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (z.value)) | ((y.value) \u0026 ~(z.value)))\n\n\ndef J(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ ((y.value) | ~(z.value)))\n\n\n##### The ten basic operations FF() through III() #####\ndef FF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x5A827999).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6ED9EBA1).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef II(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x8F1BBCDC).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0xA953FD4E).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef FFF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GGG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x7A6D76E9).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HHH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6D703EF3).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef III(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x5C4DD124).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0x50A28BE6).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\n# Initializes MDbuffer to \"magic constants\"\ndef MDinit(MDbuf: list[u32]):\n MDbuf[0] = c_uint32(0x67452301)\n MDbuf[1] = c_uint32(0xEFCDAB89)\n MDbuf[2] = c_uint32(0x98BADCFE)\n MDbuf[3] = c_uint32(0x10325476)\n MDbuf[4] = c_uint32(0xC3D2E1F0)\n\n\n##### The compression function. Transforms MDbuf using message bytes X[0] through X[15] #####\ndef compress(MDbuf: list[u32], X: list[u32]):\n aa = c_uint32(MDbuf[0].value)\n bb = c_uint32(MDbuf[1].value)\n cc = c_uint32(MDbuf[2].value)\n dd = c_uint32(MDbuf[3].value)\n ee = c_uint32(MDbuf[4].value)\n\n aaa = c_uint32(MDbuf[0].value)\n bbb = c_uint32(MDbuf[1].value)\n ccc = c_uint32(MDbuf[2].value)\n ddd = c_uint32(MDbuf[3].value)\n eee = c_uint32(MDbuf[4].value)\n\n # round 1\n FF(aa, bb, cc, dd, ee, X[0], c_uint32(11))\n FF(ee, aa, bb, cc, dd, X[1], c_uint32(14))\n FF(dd, ee, aa, bb, cc, X[2], c_uint32(15))\n FF(cc, dd, ee, aa, bb, X[3], c_uint32(12))\n FF(bb, cc, dd, ee, aa, X[4], c_uint32(5))\n FF(aa, bb, cc, dd, ee, X[5], c_uint32(8))\n FF(ee, aa, bb, cc, dd, X[6], c_uint32(7))\n FF(dd, ee, aa, bb, cc, X[7], c_uint32(9))\n FF(cc, dd, ee, aa, bb, X[8], c_uint32(11))\n FF(bb, cc, dd, ee, aa, X[9], c_uint32(13))\n FF(aa, bb, cc, dd, ee, X[10], c_uint32(14))\n FF(ee, aa, bb, cc, dd, X[11], c_uint32(15))\n FF(dd, ee, aa, bb, cc, X[12], c_uint32(6))\n FF(cc, dd, ee, aa, bb, X[13], c_uint32(7))\n FF(bb, cc, dd, ee, aa, X[14], c_uint32(9))\n FF(aa, bb, cc, dd, ee, X[15], c_uint32(8))\n\n # round 2\n GG(ee, aa, bb, cc, dd, X[7], c_uint32(7))\n GG(dd, ee, aa, bb, cc, X[4], c_uint32(6))\n GG(cc, dd, ee, aa, bb, X[13], c_uint32(8))\n GG(bb, cc, dd, ee, aa, X[1], c_uint32(13))\n GG(aa, bb, cc, dd, ee, X[10], c_uint32(11))\n GG(ee, aa, bb, cc, dd, X[6], c_uint32(9))\n GG(dd, ee, aa, bb, cc, X[15], c_uint32(7))\n GG(cc, dd, ee, aa, bb, X[3], c_uint32(15))\n GG(bb, cc, dd, ee, aa, X[12], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[0], c_uint32(12))\n GG(ee, aa, bb, cc, dd, X[9], c_uint32(15))\n GG(dd, ee, aa, bb, cc, X[5], c_uint32(9))\n GG(cc, dd, ee, aa, bb, X[2], c_uint32(11))\n GG(bb, cc, dd, ee, aa, X[14], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[11], c_uint32(13))\n GG(ee, aa, bb, cc, dd, X[8], c_uint32(12))\n\n # round 3\n HH(dd, ee, aa, bb, cc, X[3], c_uint32(11))\n HH(cc, dd, ee, aa, bb, X[10], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[14], c_uint32(6))\n HH(aa, bb, cc, dd, ee, X[4], c_uint32(7))\n HH(ee, aa, bb, cc, dd, X[9], c_uint32(14))\n HH(dd, ee, aa, bb, cc, X[15], c_uint32(9))\n HH(cc, dd, ee, aa, bb, X[8], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[1], c_uint32(15))\n HH(aa, bb, cc, dd, ee, X[2], c_uint32(14))\n HH(ee, aa, bb, cc, dd, X[7], c_uint32(8))\n HH(dd, ee, aa, bb, cc, X[0], c_uint32(13))\n HH(cc, dd, ee, aa, bb, X[6], c_uint32(6))\n HH(bb, cc, dd, ee, aa, X[13], c_uint32(5))\n HH(aa, bb, cc, dd, ee, X[11], c_uint32(12))\n HH(ee, aa, bb, cc, dd, X[5], c_uint32(7))\n HH(dd, ee, aa, bb, cc, X[12], c_uint32(5))\n\n # round 4\n II(cc, dd, ee, aa, bb, X[1], c_uint32(11))\n II(bb, cc, dd, ee, aa, X[9], c_uint32(12))\n II(aa, bb, cc, dd, ee, X[11], c_uint32(14))\n II(ee, aa, bb, cc, dd, X[10], c_uint32(15))\n II(dd, ee, aa, bb, cc, X[0], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[8], c_uint32(15))\n II(bb, cc, dd, ee, aa, X[12], c_uint32(9))\n II(aa, bb, cc, dd, ee, X[4], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[13], c_uint32(9))\n II(dd, ee, aa, bb, cc, X[3], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[7], c_uint32(5))\n II(bb, cc, dd, ee, aa, X[15], c_uint32(6))\n II(aa, bb, cc, dd, ee, X[14], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[5], c_uint32(6))\n II(dd, ee, aa, bb, cc, X[6], c_uint32(5))\n II(cc, dd, ee, aa, bb, X[2], c_uint32(12))\n\n # round 5\n JJ(bb, cc, dd, ee, aa, X[4], c_uint32(9))\n JJ(aa, bb, cc, dd, ee, X[0], c_uint32(15))\n JJ(ee, aa, bb, cc, dd, X[5], c_uint32(5))\n JJ(dd, ee, aa, bb, cc, X[9], c_uint32(11))\n JJ(cc, dd, ee, aa, bb, X[7], c_uint32(6))\n JJ(bb, cc, dd, ee, aa, X[12], c_uint32(8))\n JJ(aa, bb, cc, dd, ee, X[2], c_uint32(13))\n JJ(ee, aa, bb, cc, dd, X[10], c_uint32(12))\n JJ(dd, ee, aa, bb, cc, X[14], c_uint32(5))\n JJ(cc, dd, ee, aa, bb, X[1], c_uint32(12))\n JJ(bb, cc, dd, ee, aa, X[3], c_uint32(13))\n JJ(aa, bb, cc, dd, ee, X[8], c_uint32(14))\n JJ(ee, aa, bb, cc, dd, X[11], c_uint32(11))\n JJ(dd, ee, aa, bb, cc, X[6], c_uint32(8))\n JJ(cc, dd, ee, aa, bb, X[15], c_uint32(5))\n JJ(bb, cc, dd, ee, aa, X[13], c_uint32(6))\n\n # parallel round 1\n JJJ(aaa, bbb, ccc, ddd, eee, X[5], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[14], c_uint32(9))\n JJJ(ddd, eee, aaa, bbb, ccc, X[7], c_uint32(9))\n JJJ(ccc, ddd, eee, aaa, bbb, X[0], c_uint32(11))\n JJJ(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(13))\n JJJ(aaa, bbb, ccc, ddd, eee, X[2], c_uint32(15))\n JJJ(eee, aaa, bbb, ccc, ddd, X[11], c_uint32(15))\n JJJ(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(5))\n JJJ(ccc, ddd, eee, aaa, bbb, X[13], c_uint32(7))\n JJJ(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(7))\n JJJ(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(11))\n JJJ(ddd, eee, aaa, bbb, ccc, X[1], c_uint32(14))\n JJJ(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(14))\n JJJ(bbb, ccc, ddd, eee, aaa, X[3], c_uint32(12))\n JJJ(aaa, bbb, ccc, ddd, eee, X[12], c_uint32(6))\n\n # parallel round 2\n III(eee, aaa, bbb, ccc, ddd, X[6], c_uint32(9))\n III(ddd, eee, aaa, bbb, ccc, X[11], c_uint32(13))\n III(ccc, ddd, eee, aaa, bbb, X[3], c_uint32(15))\n III(bbb, ccc, ddd, eee, aaa, X[7], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(12))\n III(eee, aaa, bbb, ccc, ddd, X[13], c_uint32(8))\n III(ddd, eee, aaa, bbb, ccc, X[5], c_uint32(9))\n III(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(11))\n III(bbb, ccc, ddd, eee, aaa, X[14], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(7))\n III(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(12))\n III(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(7))\n III(ccc, ddd, eee, aaa, bbb, X[4], c_uint32(6))\n III(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(15))\n III(aaa, bbb, ccc, ddd, eee, X[1], c_uint32(13))\n III(eee, aaa, bbb, ccc, ddd, X[2], c_uint32(11))\n\n # parallel round 3\n HHH(ddd, eee, aaa, bbb, ccc, X[15], c_uint32(9))\n HHH(ccc, ddd, eee, aaa, bbb, X[5], c_uint32(7))\n HHH(bbb, ccc, ddd, eee, aaa, X[1], c_uint32(15))\n HHH(aaa, bbb, ccc, ddd, eee, X[3], c_uint32(11))\n HHH(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(8))\n HHH(ddd, eee, aaa, bbb, ccc, X[14], c_uint32(6))\n HHH(ccc, ddd, eee, aaa, bbb, X[6], c_uint32(6))\n HHH(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(14))\n HHH(aaa, bbb, ccc, ddd, eee, X[11], c_uint32(12))\n HHH(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(13))\n HHH(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(5))\n HHH(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(14))\n HHH(bbb, ccc, ddd, eee, aaa, X[10], c_uint32(13))\n HHH(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(13))\n HHH(eee, aaa, bbb, ccc, ddd, X[4], c_uint32(7))\n HHH(ddd, eee, aaa, bbb, ccc, X[13], c_uint32(5))\n\n # parallel round 4\n GGG(ccc, ddd, eee, aaa, bbb, X[8], c_uint32(15))\n GGG(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(5))\n GGG(aaa, bbb, ccc, ddd, eee, X[4], c_uint32(8))\n GGG(eee, aaa, bbb, ccc, ddd, X[1], c_uint32(11))\n GGG(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(14))\n GGG(ccc, ddd, eee, aaa, bbb, X[11], c_uint32(14))\n GGG(bbb, ccc, ddd, eee, aaa, X[15], c_uint32(6))\n GGG(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(14))\n GGG(eee, aaa, bbb, ccc, ddd, X[5], c_uint32(6))\n GGG(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(9))\n GGG(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(12))\n GGG(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(9))\n GGG(aaa, bbb, ccc, ddd, eee, X[9], c_uint32(12))\n GGG(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(5))\n GGG(ddd, eee, aaa, bbb, ccc, X[10], c_uint32(15))\n GGG(ccc, ddd, eee, aaa, bbb, X[14], c_uint32(8))\n\n # parallel round 5\n FFF(bbb, ccc, ddd, eee, aaa, X[12], c_uint32(8))\n FFF(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[10], c_uint32(12))\n FFF(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(9))\n FFF(ccc, ddd, eee, aaa, bbb, X[1], c_uint32(12))\n FFF(bbb, ccc, ddd, eee, aaa, X[5], c_uint32(5))\n FFF(aaa, bbb, ccc, ddd, eee, X[8], c_uint32(14))\n FFF(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(6))\n FFF(ddd, eee, aaa, bbb, ccc, X[6], c_uint32(8))\n FFF(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(13))\n FFF(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(6))\n FFF(aaa, bbb, ccc, ddd, eee, X[14], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[0], c_uint32(15))\n FFF(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(13))\n FFF(ccc, ddd, eee, aaa, bbb, X[9], c_uint32(11))\n FFF(bbb, ccc, ddd, eee, aaa, X[11], c_uint32(11))\n\n # combine results\n ddd.value += cc.value + MDbuf[1].value # final result for MDbuf[0]\n MDbuf[1].value = MDbuf[2].value + dd.value + eee.value\n MDbuf[2].value = MDbuf[3].value + ee.value + aaa.value\n MDbuf[3].value = MDbuf[4].value + aa.value + bbb.value\n MDbuf[4].value = MDbuf[0].value + bb.value + ccc.value\n MDbuf[0].value = ddd.value\n\n\n##### Puts bytes from strptr into X and pad out; appends length\n##### and finally, compresses the last block(s)\n##### note: length in bits == 8 * (lswlen + 2^32 mswlen).\n##### note: there are (lswlen mod 64) bytes left in strptr.\n\n\ndef MDfinish(MDbuf: list[u32], bytes: list[u8], lswlen: u32, mswlen: u32):\n i = c_uint32() # counter\n x = [c_uint32(0) for _ in range(16)]\n str_idx = 0\n\n # /* put bytes from strptr into X\n while i.value \u003C (lswlen.value \u0026 63):\n # byte i goes into word X[i div 4] at pos. 8*(i mod 4)\n x[i.value \u003E\u003E 2].value ^= (\n c_uint32(bytes[str_idx].value).value \u003C\u003C c_uint32((8 * (i.value \u0026 3))).value\n )\n i.value += 1\n str_idx += 1\n\n # append the bit m_n == 1\n x[(lswlen.value \u003E\u003E c_uint32(2).value) \u0026 15].value ^= c_uint32(1).value \u003C\u003C (\n 8 * (lswlen.value \u0026 3) + 7\n )\n\n if (lswlen.value \u0026 c_uint32(63).value) \u003E 55:\n compress(MDbuf, x)\n x = [c_uint32(0) for _ in range(16)]\n\n # append length in bits\n x[14].value = lswlen.value \u003C\u003C c_uint32(3).value\n x[15].value = (lswlen.value \u003E\u003E c_uint32(29).value) | (\n mswlen.value \u003C\u003C c_uint32(3).value\n )\n compress(MDbuf, x)\n\n\nRMDsize = 160\n\n\n# Converts 4 u8s into 1 u32\ndef u8_to_u32(strptr: list[u8]):\n return c_uint32(\n ((strptr[3]).value \u003C\u003C 24)\n | ((strptr[2]).value \u003C\u003C 16)\n | ((strptr[1]).value \u003C\u003C 8)\n | ((strptr[0].value))\n )\n\n\n##### Returns the RIPEMD-160 digest of the message in bytes #####\ndef RMD(message: list[u8]):\n MDbuf = [\n c_uint32(0) for _ in range((RMDsize / 32).__floor__())\n ] # contains (A, B, C, D, E)\n hashcode = [c_uint8(0) for _ in range((RMDsize / 8).__floor__())]\n # for final hash-value\n x = [c_uint32(0) for _ in range(16)] # current 16-word chunk\n i = c_uint32(0) # counter\n length = message.__len__() # length in bytes of message\n nbytes = message.__len__() # number of bytes not yet processed\n message_idx = 0\n\n MDinit(MDbuf)\n\n # process message in 16-word chunks\n\n while nbytes \u003E 63:\n i.value = 0\n while i.value \u003C 16:\n x[i.value] = u8_to_u32(message[message_idx:])\n message_idx += 4\n i.value += 1\n compress(MDbuf, x)\n nbytes -= 64\n\n # finish:\n MDfinish(MDbuf, message, c_uint32(length), c_uint32(0))\n i.value = 0\n while i.value \u003C RMDsize / 8:\n hashcode[i.value] = c_uint8(MDbuf[i.value \u003E\u003E 2].value)\n hashcode[i.value + 1] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 8)\n hashcode[i.value + 2] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 16)\n hashcode[i.value + 3] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 24)\n i.value += 4\n\n return hashcode\n\n\n##### Returns the RIPEMD-160 hash of the message in a string #####\ndef RMDstring(message: str):\n i = 0\n encoded_message: list[c_uint8] = [c_uint8(x) for x in list(message.encode())]\n\n hashcode = RMD(encoded_message)\n\n string = \"\"\n for i in hashcode:\n string += f\"{i:02x}\"\n return string\n\n"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Pure Python"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
s0wxhh30v0fj0ylao4rcudg8g92ne21
281219
281218
2026-06-07T15:39:32Z
Seller of unexistent friends
85698
281219
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "# Python translation of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.c\n# https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.h\n# and a bit of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/hashtest.c\n# It's not really pythonic but it's fine since it works.\n\n\nclass _FixedUint:\n _mask: int = 0\n\n def __init__(self, value: int = 0):\n self._value: int = value \u0026 self._mask\n\n @property\n def value(self) -\u003E int:\n return self._value\n\n @value.setter\n def value(self, v: int):\n self._value = v \u0026 self._mask\n\n def __format__(self, spec: str) -\u003E str:\n return format(self._value, spec)\n\n def __repr__(self) -\u003E str:\n return f\"{type(self).__name__}({self._value})\"\n\n\nclass c_uint8(_FixedUint):\n _mask: int = 0xFF\n\n\nclass c_uint32(_FixedUint):\n _mask: int = 0xFFFFFFFF\n\n\ntype u8 = c_uint8\ntype u32 = c_uint32\n\n\n# ROL(x, n) cyclically rotates x over n bits to the left\n# x must be of an unsigned 32 bits type and 0 \u003C= n \u003C 32.\ndef ROL(x: u32, n: u32):\n return c_uint32(((x.value) \u003C\u003C (n.value)) | ((x.value) \u003E\u003E (32 - (n.value))))\n\n\n##### The five basic functions F(), G() and H() #####\ndef F(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ (y.value) ^ (z.value))\n\n\ndef G(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (y.value)) | (~(x.value) \u0026 (z.value)))\n\n\ndef H(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) | ~(y.value)) ^ (z.value))\n\n\ndef I(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (z.value)) | ((y.value) \u0026 ~(z.value)))\n\n\ndef J(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ ((y.value) | ~(z.value)))\n\n\n##### The ten basic operations FF() through III() #####\ndef FF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x5A827999).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6ED9EBA1).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef II(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x8F1BBCDC).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0xA953FD4E).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef FFF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GGG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x7A6D76E9).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HHH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6D703EF3).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef III(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x5C4DD124).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0x50A28BE6).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\n# Initializes MDbuffer to \"magic constants\"\ndef MDinit(MDbuf: list[u32]):\n MDbuf[0] = c_uint32(0x67452301)\n MDbuf[1] = c_uint32(0xEFCDAB89)\n MDbuf[2] = c_uint32(0x98BADCFE)\n MDbuf[3] = c_uint32(0x10325476)\n MDbuf[4] = c_uint32(0xC3D2E1F0)\n\n\n##### The compression function. Transforms MDbuf using message bytes X[0] through X[15] #####\ndef compress(MDbuf: list[u32], X: list[u32]):\n aa = c_uint32(MDbuf[0].value)\n bb = c_uint32(MDbuf[1].value)\n cc = c_uint32(MDbuf[2].value)\n dd = c_uint32(MDbuf[3].value)\n ee = c_uint32(MDbuf[4].value)\n\n aaa = c_uint32(MDbuf[0].value)\n bbb = c_uint32(MDbuf[1].value)\n ccc = c_uint32(MDbuf[2].value)\n ddd = c_uint32(MDbuf[3].value)\n eee = c_uint32(MDbuf[4].value)\n\n # round 1\n FF(aa, bb, cc, dd, ee, X[0], c_uint32(11))\n FF(ee, aa, bb, cc, dd, X[1], c_uint32(14))\n FF(dd, ee, aa, bb, cc, X[2], c_uint32(15))\n FF(cc, dd, ee, aa, bb, X[3], c_uint32(12))\n FF(bb, cc, dd, ee, aa, X[4], c_uint32(5))\n FF(aa, bb, cc, dd, ee, X[5], c_uint32(8))\n FF(ee, aa, bb, cc, dd, X[6], c_uint32(7))\n FF(dd, ee, aa, bb, cc, X[7], c_uint32(9))\n FF(cc, dd, ee, aa, bb, X[8], c_uint32(11))\n FF(bb, cc, dd, ee, aa, X[9], c_uint32(13))\n FF(aa, bb, cc, dd, ee, X[10], c_uint32(14))\n FF(ee, aa, bb, cc, dd, X[11], c_uint32(15))\n FF(dd, ee, aa, bb, cc, X[12], c_uint32(6))\n FF(cc, dd, ee, aa, bb, X[13], c_uint32(7))\n FF(bb, cc, dd, ee, aa, X[14], c_uint32(9))\n FF(aa, bb, cc, dd, ee, X[15], c_uint32(8))\n\n # round 2\n GG(ee, aa, bb, cc, dd, X[7], c_uint32(7))\n GG(dd, ee, aa, bb, cc, X[4], c_uint32(6))\n GG(cc, dd, ee, aa, bb, X[13], c_uint32(8))\n GG(bb, cc, dd, ee, aa, X[1], c_uint32(13))\n GG(aa, bb, cc, dd, ee, X[10], c_uint32(11))\n GG(ee, aa, bb, cc, dd, X[6], c_uint32(9))\n GG(dd, ee, aa, bb, cc, X[15], c_uint32(7))\n GG(cc, dd, ee, aa, bb, X[3], c_uint32(15))\n GG(bb, cc, dd, ee, aa, X[12], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[0], c_uint32(12))\n GG(ee, aa, bb, cc, dd, X[9], c_uint32(15))\n GG(dd, ee, aa, bb, cc, X[5], c_uint32(9))\n GG(cc, dd, ee, aa, bb, X[2], c_uint32(11))\n GG(bb, cc, dd, ee, aa, X[14], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[11], c_uint32(13))\n GG(ee, aa, bb, cc, dd, X[8], c_uint32(12))\n\n # round 3\n HH(dd, ee, aa, bb, cc, X[3], c_uint32(11))\n HH(cc, dd, ee, aa, bb, X[10], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[14], c_uint32(6))\n HH(aa, bb, cc, dd, ee, X[4], c_uint32(7))\n HH(ee, aa, bb, cc, dd, X[9], c_uint32(14))\n HH(dd, ee, aa, bb, cc, X[15], c_uint32(9))\n HH(cc, dd, ee, aa, bb, X[8], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[1], c_uint32(15))\n HH(aa, bb, cc, dd, ee, X[2], c_uint32(14))\n HH(ee, aa, bb, cc, dd, X[7], c_uint32(8))\n HH(dd, ee, aa, bb, cc, X[0], c_uint32(13))\n HH(cc, dd, ee, aa, bb, X[6], c_uint32(6))\n HH(bb, cc, dd, ee, aa, X[13], c_uint32(5))\n HH(aa, bb, cc, dd, ee, X[11], c_uint32(12))\n HH(ee, aa, bb, cc, dd, X[5], c_uint32(7))\n HH(dd, ee, aa, bb, cc, X[12], c_uint32(5))\n\n # round 4\n II(cc, dd, ee, aa, bb, X[1], c_uint32(11))\n II(bb, cc, dd, ee, aa, X[9], c_uint32(12))\n II(aa, bb, cc, dd, ee, X[11], c_uint32(14))\n II(ee, aa, bb, cc, dd, X[10], c_uint32(15))\n II(dd, ee, aa, bb, cc, X[0], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[8], c_uint32(15))\n II(bb, cc, dd, ee, aa, X[12], c_uint32(9))\n II(aa, bb, cc, dd, ee, X[4], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[13], c_uint32(9))\n II(dd, ee, aa, bb, cc, X[3], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[7], c_uint32(5))\n II(bb, cc, dd, ee, aa, X[15], c_uint32(6))\n II(aa, bb, cc, dd, ee, X[14], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[5], c_uint32(6))\n II(dd, ee, aa, bb, cc, X[6], c_uint32(5))\n II(cc, dd, ee, aa, bb, X[2], c_uint32(12))\n\n # round 5\n JJ(bb, cc, dd, ee, aa, X[4], c_uint32(9))\n JJ(aa, bb, cc, dd, ee, X[0], c_uint32(15))\n JJ(ee, aa, bb, cc, dd, X[5], c_uint32(5))\n JJ(dd, ee, aa, bb, cc, X[9], c_uint32(11))\n JJ(cc, dd, ee, aa, bb, X[7], c_uint32(6))\n JJ(bb, cc, dd, ee, aa, X[12], c_uint32(8))\n JJ(aa, bb, cc, dd, ee, X[2], c_uint32(13))\n JJ(ee, aa, bb, cc, dd, X[10], c_uint32(12))\n JJ(dd, ee, aa, bb, cc, X[14], c_uint32(5))\n JJ(cc, dd, ee, aa, bb, X[1], c_uint32(12))\n JJ(bb, cc, dd, ee, aa, X[3], c_uint32(13))\n JJ(aa, bb, cc, dd, ee, X[8], c_uint32(14))\n JJ(ee, aa, bb, cc, dd, X[11], c_uint32(11))\n JJ(dd, ee, aa, bb, cc, X[6], c_uint32(8))\n JJ(cc, dd, ee, aa, bb, X[15], c_uint32(5))\n JJ(bb, cc, dd, ee, aa, X[13], c_uint32(6))\n\n # parallel round 1\n JJJ(aaa, bbb, ccc, ddd, eee, X[5], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[14], c_uint32(9))\n JJJ(ddd, eee, aaa, bbb, ccc, X[7], c_uint32(9))\n JJJ(ccc, ddd, eee, aaa, bbb, X[0], c_uint32(11))\n JJJ(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(13))\n JJJ(aaa, bbb, ccc, ddd, eee, X[2], c_uint32(15))\n JJJ(eee, aaa, bbb, ccc, ddd, X[11], c_uint32(15))\n JJJ(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(5))\n JJJ(ccc, ddd, eee, aaa, bbb, X[13], c_uint32(7))\n JJJ(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(7))\n JJJ(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(11))\n JJJ(ddd, eee, aaa, bbb, ccc, X[1], c_uint32(14))\n JJJ(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(14))\n JJJ(bbb, ccc, ddd, eee, aaa, X[3], c_uint32(12))\n JJJ(aaa, bbb, ccc, ddd, eee, X[12], c_uint32(6))\n\n # parallel round 2\n III(eee, aaa, bbb, ccc, ddd, X[6], c_uint32(9))\n III(ddd, eee, aaa, bbb, ccc, X[11], c_uint32(13))\n III(ccc, ddd, eee, aaa, bbb, X[3], c_uint32(15))\n III(bbb, ccc, ddd, eee, aaa, X[7], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(12))\n III(eee, aaa, bbb, ccc, ddd, X[13], c_uint32(8))\n III(ddd, eee, aaa, bbb, ccc, X[5], c_uint32(9))\n III(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(11))\n III(bbb, ccc, ddd, eee, aaa, X[14], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(7))\n III(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(12))\n III(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(7))\n III(ccc, ddd, eee, aaa, bbb, X[4], c_uint32(6))\n III(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(15))\n III(aaa, bbb, ccc, ddd, eee, X[1], c_uint32(13))\n III(eee, aaa, bbb, ccc, ddd, X[2], c_uint32(11))\n\n # parallel round 3\n HHH(ddd, eee, aaa, bbb, ccc, X[15], c_uint32(9))\n HHH(ccc, ddd, eee, aaa, bbb, X[5], c_uint32(7))\n HHH(bbb, ccc, ddd, eee, aaa, X[1], c_uint32(15))\n HHH(aaa, bbb, ccc, ddd, eee, X[3], c_uint32(11))\n HHH(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(8))\n HHH(ddd, eee, aaa, bbb, ccc, X[14], c_uint32(6))\n HHH(ccc, ddd, eee, aaa, bbb, X[6], c_uint32(6))\n HHH(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(14))\n HHH(aaa, bbb, ccc, ddd, eee, X[11], c_uint32(12))\n HHH(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(13))\n HHH(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(5))\n HHH(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(14))\n HHH(bbb, ccc, ddd, eee, aaa, X[10], c_uint32(13))\n HHH(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(13))\n HHH(eee, aaa, bbb, ccc, ddd, X[4], c_uint32(7))\n HHH(ddd, eee, aaa, bbb, ccc, X[13], c_uint32(5))\n\n # parallel round 4\n GGG(ccc, ddd, eee, aaa, bbb, X[8], c_uint32(15))\n GGG(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(5))\n GGG(aaa, bbb, ccc, ddd, eee, X[4], c_uint32(8))\n GGG(eee, aaa, bbb, ccc, ddd, X[1], c_uint32(11))\n GGG(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(14))\n GGG(ccc, ddd, eee, aaa, bbb, X[11], c_uint32(14))\n GGG(bbb, ccc, ddd, eee, aaa, X[15], c_uint32(6))\n GGG(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(14))\n GGG(eee, aaa, bbb, ccc, ddd, X[5], c_uint32(6))\n GGG(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(9))\n GGG(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(12))\n GGG(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(9))\n GGG(aaa, bbb, ccc, ddd, eee, X[9], c_uint32(12))\n GGG(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(5))\n GGG(ddd, eee, aaa, bbb, ccc, X[10], c_uint32(15))\n GGG(ccc, ddd, eee, aaa, bbb, X[14], c_uint32(8))\n\n # parallel round 5\n FFF(bbb, ccc, ddd, eee, aaa, X[12], c_uint32(8))\n FFF(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[10], c_uint32(12))\n FFF(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(9))\n FFF(ccc, ddd, eee, aaa, bbb, X[1], c_uint32(12))\n FFF(bbb, ccc, ddd, eee, aaa, X[5], c_uint32(5))\n FFF(aaa, bbb, ccc, ddd, eee, X[8], c_uint32(14))\n FFF(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(6))\n FFF(ddd, eee, aaa, bbb, ccc, X[6], c_uint32(8))\n FFF(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(13))\n FFF(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(6))\n FFF(aaa, bbb, ccc, ddd, eee, X[14], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[0], c_uint32(15))\n FFF(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(13))\n FFF(ccc, ddd, eee, aaa, bbb, X[9], c_uint32(11))\n FFF(bbb, ccc, ddd, eee, aaa, X[11], c_uint32(11))\n\n # combine results\n ddd.value += cc.value + MDbuf[1].value # final result for MDbuf[0]\n MDbuf[1].value = MDbuf[2].value + dd.value + eee.value\n MDbuf[2].value = MDbuf[3].value + ee.value + aaa.value\n MDbuf[3].value = MDbuf[4].value + aa.value + bbb.value\n MDbuf[4].value = MDbuf[0].value + bb.value + ccc.value\n MDbuf[0].value = ddd.value\n\n\n##### Puts bytes from strptr into X and pad out; appends length\n##### and finally, compresses the last block(s)\n##### note: length in bits == 8 * (lswlen + 2^32 mswlen).\n##### note: there are (lswlen mod 64) bytes left in strptr.\n\n\ndef MDfinish(MDbuf: list[u32], bytes: list[u8], lswlen: u32, mswlen: u32):\n i = c_uint32() # counter\n x = [c_uint32(0) for _ in range(16)]\n str_idx = 0\n\n # /* put bytes from strptr into X\n while i.value \u003C (lswlen.value \u0026 63):\n # byte i goes into word X[i div 4] at pos. 8*(i mod 4)\n x[i.value \u003E\u003E 2].value ^= (\n c_uint32(bytes[str_idx].value).value \u003C\u003C c_uint32((8 * (i.value \u0026 3))).value\n )\n i.value += 1\n str_idx += 1\n\n # append the bit m_n == 1\n x[(lswlen.value \u003E\u003E c_uint32(2).value) \u0026 15].value ^= c_uint32(1).value \u003C\u003C (\n 8 * (lswlen.value \u0026 3) + 7\n )\n\n if (lswlen.value \u0026 c_uint32(63).value) \u003E 55:\n compress(MDbuf, x)\n x = [c_uint32(0) for _ in range(16)]\n\n # append length in bits\n x[14].value = lswlen.value \u003C\u003C c_uint32(3).value\n x[15].value = (lswlen.value \u003E\u003E c_uint32(29).value) | (\n mswlen.value \u003C\u003C c_uint32(3).value\n )\n compress(MDbuf, x)\n\n\nRMDsize = 160\n\n\n# Converts 4 u8s into 1 u32\ndef u8_to_u32(strptr: list[u8]):\n return c_uint32(\n ((strptr[3]).value \u003C\u003C 24)\n | ((strptr[2]).value \u003C\u003C 16)\n | ((strptr[1]).value \u003C\u003C 8)\n | ((strptr[0].value))\n )\n\n\n##### Returns the RIPEMD-160 digest of the message in bytes #####\ndef RMD(message: list[u8]):\n MDbuf = [\n c_uint32(0) for _ in range((RMDsize / 32).__floor__())\n ] # contains (A, B, C, D, E)\n hashcode = [c_uint8(0) for _ in range((RMDsize / 8).__floor__())]\n # for final hash-value\n x = [c_uint32(0) for _ in range(16)] # current 16-word chunk\n i = c_uint32(0) # counter\n length = message.__len__() # length in bytes of message\n nbytes = message.__len__() # number of bytes not yet processed\n message_idx = 0\n\n MDinit(MDbuf)\n\n # process message in 16-word chunks\n\n while nbytes \u003E 63:\n i.value = 0\n while i.value \u003C 16:\n x[i.value] = u8_to_u32(message[message_idx:])\n message_idx += 4\n i.value += 1\n compress(MDbuf, x)\n nbytes -= 64\n\n # finish:\n MDfinish(MDbuf, message, c_uint32(length), c_uint32(0))\n i.value = 0\n while i.value \u003C RMDsize / 8:\n hashcode[i.value] = c_uint8(MDbuf[i.value \u003E\u003E 2].value)\n hashcode[i.value + 1] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 8)\n hashcode[i.value + 2] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 16)\n hashcode[i.value + 3] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 24)\n i.value += 4\n\n return hashcode\n\n\n##### Returns the RIPEMD-160 hash of the message in a string #####\ndef RMDstring(message: str):\n i = 0\n encoded_message: list[c_uint8] = [c_uint8(x) for x in list(message.encode())]\n\n hashcode = RMD(encoded_message)\n\n string = \"\"\n for i in hashcode:\n string += f\"{i:02x}\"\n return string\n\ndef Z10139(Z10139K1):\n\treturn RMDstring(Z10139K1)"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Pure Python"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
6i9skwuk1pnne5sqkzjcgwhs27cbe47
281220
281219
2026-06-07T15:41:19Z
Seller of unexistent friends
85698
281220
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "# Python translation of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.c\n# https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.h\n# and a bit of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/hashtest.c\n# It's not really pythonic but it's fine since it works.\n\n\nclass _FixedUint:\n _mask: int = 0\n\n def __init__(self, value: int = 0):\n self._value: int = value \u0026 self._mask\n\n @property\n def value(self) -\u003E int:\n return self._value\n\n @value.setter\n def value(self, v: int):\n self._value = v \u0026 self._mask\n\n def __format__(self, spec: str) -\u003E str:\n return format(self._value, spec)\n\n def __repr__(self) -\u003E str:\n return f\"{type(self).__name__}({self._value})\"\n\n\nclass c_uint8(_FixedUint):\n _mask: int = 0xFF\n\n\nclass c_uint32(_FixedUint):\n _mask: int = 0xFFFFFFFF\n\n\ntype u8 = c_uint8\ntype u32 = c_uint32\n\n\n# ROL(x, n) cyclically rotates x over n bits to the left\n# x must be of an unsigned 32 bits type and 0 \u003C= n \u003C 32.\ndef ROL(x: u32, n: u32):\n return c_uint32(((x.value) \u003C\u003C (n.value)) | ((x.value) \u003E\u003E (32 - (n.value))))\n\n\n##### The five basic functions F(), G() and H() #####\ndef F(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ (y.value) ^ (z.value))\n\n\ndef G(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (y.value)) | (~(x.value) \u0026 (z.value)))\n\n\ndef H(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) | ~(y.value)) ^ (z.value))\n\n\ndef I(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (z.value)) | ((y.value) \u0026 ~(z.value)))\n\n\ndef J(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ ((y.value) | ~(z.value)))\n\n\n##### The ten basic operations FF() through III() #####\ndef FF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x5A827999).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6ED9EBA1).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef II(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x8F1BBCDC).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0xA953FD4E).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef FFF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GGG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x7A6D76E9).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HHH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6D703EF3).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef III(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x5C4DD124).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0x50A28BE6).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\n# Initializes MDbuffer to \"magic constants\"\ndef MDinit(MDbuf: list[u32]):\n MDbuf[0] = c_uint32(0x67452301)\n MDbuf[1] = c_uint32(0xEFCDAB89)\n MDbuf[2] = c_uint32(0x98BADCFE)\n MDbuf[3] = c_uint32(0x10325476)\n MDbuf[4] = c_uint32(0xC3D2E1F0)\n\n\n##### The compression function. Transforms MDbuf using message bytes X[0] through X[15] #####\ndef compress(MDbuf: list[u32], X: list[u32]):\n aa = c_uint32(MDbuf[0].value)\n bb = c_uint32(MDbuf[1].value)\n cc = c_uint32(MDbuf[2].value)\n dd = c_uint32(MDbuf[3].value)\n ee = c_uint32(MDbuf[4].value)\n\n aaa = c_uint32(MDbuf[0].value)\n bbb = c_uint32(MDbuf[1].value)\n ccc = c_uint32(MDbuf[2].value)\n ddd = c_uint32(MDbuf[3].value)\n eee = c_uint32(MDbuf[4].value)\n\n # round 1\n FF(aa, bb, cc, dd, ee, X[0], c_uint32(11))\n FF(ee, aa, bb, cc, dd, X[1], c_uint32(14))\n FF(dd, ee, aa, bb, cc, X[2], c_uint32(15))\n FF(cc, dd, ee, aa, bb, X[3], c_uint32(12))\n FF(bb, cc, dd, ee, aa, X[4], c_uint32(5))\n FF(aa, bb, cc, dd, ee, X[5], c_uint32(8))\n FF(ee, aa, bb, cc, dd, X[6], c_uint32(7))\n FF(dd, ee, aa, bb, cc, X[7], c_uint32(9))\n FF(cc, dd, ee, aa, bb, X[8], c_uint32(11))\n FF(bb, cc, dd, ee, aa, X[9], c_uint32(13))\n FF(aa, bb, cc, dd, ee, X[10], c_uint32(14))\n FF(ee, aa, bb, cc, dd, X[11], c_uint32(15))\n FF(dd, ee, aa, bb, cc, X[12], c_uint32(6))\n FF(cc, dd, ee, aa, bb, X[13], c_uint32(7))\n FF(bb, cc, dd, ee, aa, X[14], c_uint32(9))\n FF(aa, bb, cc, dd, ee, X[15], c_uint32(8))\n\n # round 2\n GG(ee, aa, bb, cc, dd, X[7], c_uint32(7))\n GG(dd, ee, aa, bb, cc, X[4], c_uint32(6))\n GG(cc, dd, ee, aa, bb, X[13], c_uint32(8))\n GG(bb, cc, dd, ee, aa, X[1], c_uint32(13))\n GG(aa, bb, cc, dd, ee, X[10], c_uint32(11))\n GG(ee, aa, bb, cc, dd, X[6], c_uint32(9))\n GG(dd, ee, aa, bb, cc, X[15], c_uint32(7))\n GG(cc, dd, ee, aa, bb, X[3], c_uint32(15))\n GG(bb, cc, dd, ee, aa, X[12], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[0], c_uint32(12))\n GG(ee, aa, bb, cc, dd, X[9], c_uint32(15))\n GG(dd, ee, aa, bb, cc, X[5], c_uint32(9))\n GG(cc, dd, ee, aa, bb, X[2], c_uint32(11))\n GG(bb, cc, dd, ee, aa, X[14], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[11], c_uint32(13))\n GG(ee, aa, bb, cc, dd, X[8], c_uint32(12))\n\n # round 3\n HH(dd, ee, aa, bb, cc, X[3], c_uint32(11))\n HH(cc, dd, ee, aa, bb, X[10], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[14], c_uint32(6))\n HH(aa, bb, cc, dd, ee, X[4], c_uint32(7))\n HH(ee, aa, bb, cc, dd, X[9], c_uint32(14))\n HH(dd, ee, aa, bb, cc, X[15], c_uint32(9))\n HH(cc, dd, ee, aa, bb, X[8], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[1], c_uint32(15))\n HH(aa, bb, cc, dd, ee, X[2], c_uint32(14))\n HH(ee, aa, bb, cc, dd, X[7], c_uint32(8))\n HH(dd, ee, aa, bb, cc, X[0], c_uint32(13))\n HH(cc, dd, ee, aa, bb, X[6], c_uint32(6))\n HH(bb, cc, dd, ee, aa, X[13], c_uint32(5))\n HH(aa, bb, cc, dd, ee, X[11], c_uint32(12))\n HH(ee, aa, bb, cc, dd, X[5], c_uint32(7))\n HH(dd, ee, aa, bb, cc, X[12], c_uint32(5))\n\n # round 4\n II(cc, dd, ee, aa, bb, X[1], c_uint32(11))\n II(bb, cc, dd, ee, aa, X[9], c_uint32(12))\n II(aa, bb, cc, dd, ee, X[11], c_uint32(14))\n II(ee, aa, bb, cc, dd, X[10], c_uint32(15))\n II(dd, ee, aa, bb, cc, X[0], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[8], c_uint32(15))\n II(bb, cc, dd, ee, aa, X[12], c_uint32(9))\n II(aa, bb, cc, dd, ee, X[4], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[13], c_uint32(9))\n II(dd, ee, aa, bb, cc, X[3], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[7], c_uint32(5))\n II(bb, cc, dd, ee, aa, X[15], c_uint32(6))\n II(aa, bb, cc, dd, ee, X[14], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[5], c_uint32(6))\n II(dd, ee, aa, bb, cc, X[6], c_uint32(5))\n II(cc, dd, ee, aa, bb, X[2], c_uint32(12))\n\n # round 5\n JJ(bb, cc, dd, ee, aa, X[4], c_uint32(9))\n JJ(aa, bb, cc, dd, ee, X[0], c_uint32(15))\n JJ(ee, aa, bb, cc, dd, X[5], c_uint32(5))\n JJ(dd, ee, aa, bb, cc, X[9], c_uint32(11))\n JJ(cc, dd, ee, aa, bb, X[7], c_uint32(6))\n JJ(bb, cc, dd, ee, aa, X[12], c_uint32(8))\n JJ(aa, bb, cc, dd, ee, X[2], c_uint32(13))\n JJ(ee, aa, bb, cc, dd, X[10], c_uint32(12))\n JJ(dd, ee, aa, bb, cc, X[14], c_uint32(5))\n JJ(cc, dd, ee, aa, bb, X[1], c_uint32(12))\n JJ(bb, cc, dd, ee, aa, X[3], c_uint32(13))\n JJ(aa, bb, cc, dd, ee, X[8], c_uint32(14))\n JJ(ee, aa, bb, cc, dd, X[11], c_uint32(11))\n JJ(dd, ee, aa, bb, cc, X[6], c_uint32(8))\n JJ(cc, dd, ee, aa, bb, X[15], c_uint32(5))\n JJ(bb, cc, dd, ee, aa, X[13], c_uint32(6))\n\n # parallel round 1\n JJJ(aaa, bbb, ccc, ddd, eee, X[5], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[14], c_uint32(9))\n JJJ(ddd, eee, aaa, bbb, ccc, X[7], c_uint32(9))\n JJJ(ccc, ddd, eee, aaa, bbb, X[0], c_uint32(11))\n JJJ(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(13))\n JJJ(aaa, bbb, ccc, ddd, eee, X[2], c_uint32(15))\n JJJ(eee, aaa, bbb, ccc, ddd, X[11], c_uint32(15))\n JJJ(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(5))\n JJJ(ccc, ddd, eee, aaa, bbb, X[13], c_uint32(7))\n JJJ(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(7))\n JJJ(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(11))\n JJJ(ddd, eee, aaa, bbb, ccc, X[1], c_uint32(14))\n JJJ(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(14))\n JJJ(bbb, ccc, ddd, eee, aaa, X[3], c_uint32(12))\n JJJ(aaa, bbb, ccc, ddd, eee, X[12], c_uint32(6))\n\n # parallel round 2\n III(eee, aaa, bbb, ccc, ddd, X[6], c_uint32(9))\n III(ddd, eee, aaa, bbb, ccc, X[11], c_uint32(13))\n III(ccc, ddd, eee, aaa, bbb, X[3], c_uint32(15))\n III(bbb, ccc, ddd, eee, aaa, X[7], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(12))\n III(eee, aaa, bbb, ccc, ddd, X[13], c_uint32(8))\n III(ddd, eee, aaa, bbb, ccc, X[5], c_uint32(9))\n III(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(11))\n III(bbb, ccc, ddd, eee, aaa, X[14], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(7))\n III(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(12))\n III(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(7))\n III(ccc, ddd, eee, aaa, bbb, X[4], c_uint32(6))\n III(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(15))\n III(aaa, bbb, ccc, ddd, eee, X[1], c_uint32(13))\n III(eee, aaa, bbb, ccc, ddd, X[2], c_uint32(11))\n\n # parallel round 3\n HHH(ddd, eee, aaa, bbb, ccc, X[15], c_uint32(9))\n HHH(ccc, ddd, eee, aaa, bbb, X[5], c_uint32(7))\n HHH(bbb, ccc, ddd, eee, aaa, X[1], c_uint32(15))\n HHH(aaa, bbb, ccc, ddd, eee, X[3], c_uint32(11))\n HHH(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(8))\n HHH(ddd, eee, aaa, bbb, ccc, X[14], c_uint32(6))\n HHH(ccc, ddd, eee, aaa, bbb, X[6], c_uint32(6))\n HHH(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(14))\n HHH(aaa, bbb, ccc, ddd, eee, X[11], c_uint32(12))\n HHH(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(13))\n HHH(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(5))\n HHH(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(14))\n HHH(bbb, ccc, ddd, eee, aaa, X[10], c_uint32(13))\n HHH(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(13))\n HHH(eee, aaa, bbb, ccc, ddd, X[4], c_uint32(7))\n HHH(ddd, eee, aaa, bbb, ccc, X[13], c_uint32(5))\n\n # parallel round 4\n GGG(ccc, ddd, eee, aaa, bbb, X[8], c_uint32(15))\n GGG(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(5))\n GGG(aaa, bbb, ccc, ddd, eee, X[4], c_uint32(8))\n GGG(eee, aaa, bbb, ccc, ddd, X[1], c_uint32(11))\n GGG(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(14))\n GGG(ccc, ddd, eee, aaa, bbb, X[11], c_uint32(14))\n GGG(bbb, ccc, ddd, eee, aaa, X[15], c_uint32(6))\n GGG(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(14))\n GGG(eee, aaa, bbb, ccc, ddd, X[5], c_uint32(6))\n GGG(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(9))\n GGG(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(12))\n GGG(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(9))\n GGG(aaa, bbb, ccc, ddd, eee, X[9], c_uint32(12))\n GGG(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(5))\n GGG(ddd, eee, aaa, bbb, ccc, X[10], c_uint32(15))\n GGG(ccc, ddd, eee, aaa, bbb, X[14], c_uint32(8))\n\n # parallel round 5\n FFF(bbb, ccc, ddd, eee, aaa, X[12], c_uint32(8))\n FFF(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[10], c_uint32(12))\n FFF(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(9))\n FFF(ccc, ddd, eee, aaa, bbb, X[1], c_uint32(12))\n FFF(bbb, ccc, ddd, eee, aaa, X[5], c_uint32(5))\n FFF(aaa, bbb, ccc, ddd, eee, X[8], c_uint32(14))\n FFF(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(6))\n FFF(ddd, eee, aaa, bbb, ccc, X[6], c_uint32(8))\n FFF(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(13))\n FFF(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(6))\n FFF(aaa, bbb, ccc, ddd, eee, X[14], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[0], c_uint32(15))\n FFF(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(13))\n FFF(ccc, ddd, eee, aaa, bbb, X[9], c_uint32(11))\n FFF(bbb, ccc, ddd, eee, aaa, X[11], c_uint32(11))\n\n # combine results\n ddd.value += cc.value + MDbuf[1].value # final result for MDbuf[0]\n MDbuf[1].value = MDbuf[2].value + dd.value + eee.value\n MDbuf[2].value = MDbuf[3].value + ee.value + aaa.value\n MDbuf[3].value = MDbuf[4].value + aa.value + bbb.value\n MDbuf[4].value = MDbuf[0].value + bb.value + ccc.value\n MDbuf[0].value = ddd.value\n\n\n##### Puts bytes from strptr into X and pad out; appends length\n##### and finally, compresses the last block(s)\n##### note: length in bits == 8 * (lswlen + 2^32 mswlen).\n##### note: there are (lswlen mod 64) bytes left in strptr.\n\n\ndef MDfinish(MDbuf: list[u32], bytes: list[u8], lswlen: u32, mswlen: u32):\n i = c_uint32() # counter\n x = [c_uint32(0) for _ in range(16)]\n str_idx = 0\n\n # /* put bytes from strptr into X\n while i.value \u003C (lswlen.value \u0026 63):\n # byte i goes into word X[i div 4] at pos. 8*(i mod 4)\n x[i.value \u003E\u003E 2].value ^= (\n c_uint32(bytes[str_idx].value).value \u003C\u003C c_uint32((8 * (i.value \u0026 3))).value\n )\n i.value += 1\n str_idx += 1\n\n # append the bit m_n == 1\n x[(lswlen.value \u003E\u003E c_uint32(2).value) \u0026 15].value ^= c_uint32(1).value \u003C\u003C (\n 8 * (lswlen.value \u0026 3) + 7\n )\n\n if (lswlen.value \u0026 c_uint32(63).value) \u003E 55:\n compress(MDbuf, x)\n x = [c_uint32(0) for _ in range(16)]\n\n # append length in bits\n x[14].value = lswlen.value \u003C\u003C c_uint32(3).value\n x[15].value = (lswlen.value \u003E\u003E c_uint32(29).value) | (\n mswlen.value \u003C\u003C c_uint32(3).value\n )\n compress(MDbuf, x)\n\n\nRMDsize = 160\n\n\n# Converts 4 u8s into 1 u32\ndef u8_to_u32(strptr: list[u8]):\n return c_uint32(\n ((strptr[3]).value \u003C\u003C 24)\n | ((strptr[2]).value \u003C\u003C 16)\n | ((strptr[1]).value \u003C\u003C 8)\n | ((strptr[0].value))\n )\n\n\n##### Returns the RIPEMD-160 digest of the message in bytes #####\ndef RMD(message: list[u8]):\n MDbuf = [\n c_uint32(0) for _ in range((RMDsize / 32).__floor__())\n ] # contains (A, B, C, D, E)\n hashcode = [c_uint8(0) for _ in range((RMDsize / 8).__floor__())]\n # for final hash-value\n x = [c_uint32(0) for _ in range(16)] # current 16-word chunk\n i = c_uint32(0) # counter\n length = message.__len__() # length in bytes of message\n nbytes = message.__len__() # number of bytes not yet processed\n message_idx = 0\n\n MDinit(MDbuf)\n\n # process message in 16-word chunks\n\n while nbytes \u003E 63:\n i.value = 0\n while i.value \u003C 16:\n x[i.value] = u8_to_u32(message[message_idx:])\n message_idx += 4\n i.value += 1\n compress(MDbuf, x)\n nbytes -= 64\n\n # finish:\n MDfinish(MDbuf, message, c_uint32(length), c_uint32(0))\n i.value = 0\n while i.value \u003C RMDsize / 8:\n hashcode[i.value] = c_uint8(MDbuf[i.value \u003E\u003E 2].value)\n hashcode[i.value + 1] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 8)\n hashcode[i.value + 2] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 16)\n hashcode[i.value + 3] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 24)\n i.value += 4\n\n return hashcode\n\n\n##### Returns the RIPEMD-160 hash of the message in a string #####\ndef RMDstring(message: str):\n i = 0\n encoded_message: list[c_uint8] = [c_uint8(x) for x in list(message.encode())]\n\n hashcode = RMD(encoded_message)\n\n string = \"\"\n for i in hashcode:\n string += f\"{i:02x}\"\n return string\n\n\ndef Z10139(Z10139K1):\n return RMDstring(Z10139K1)\n"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Pure Python"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
d4wlhjozrekveehhinwl69g548hko13
281221
281220
2026-06-07T15:56:32Z
Seller of unexistent friends
85698
281221
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "# Python translation of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.c\n# https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.h\n# and a bit of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/hashtest.c\n# It's not really pythonic but it's fine since it works.\n\n\nclass _FixedUint:\n _mask: int = 0\n\n def __init__(self, value: int = 0):\n self._value: int = value \u0026 self._mask\n\n @property\n def value(self) -\u003E int:\n return self._value\n\n @value.setter\n def value(self, v: int):\n self._value = v \u0026 self._mask\n\n def __format__(self, spec: str) -\u003E str:\n return format(self._value, spec)\n\n def __repr__(self) -\u003E str:\n return f\"{type(self).__name__}({self._value})\"\n\n\nclass c_uint8(_FixedUint):\n _mask: int = 0xFF\n\n\nclass c_uint32(_FixedUint):\n _mask: int = 0xFFFFFFFF\n\n\ntype u8 = c_uint8\ntype u32 = c_uint32\n\n\n# ROL(x, n) cyclically rotates x over n bits to the left\n# x must be of an unsigned 32 bits type and 0 \u003C= n \u003C 32.\ndef ROL(x: u32, n: u32):\n return c_uint32(((x.value) \u003C\u003C (n.value)) | ((x.value) \u003E\u003E (32 - (n.value))))\n\n\n##### The five basic functions F(), G() and H() #####\ndef F(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ (y.value) ^ (z.value))\n\n\ndef G(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (y.value)) | (~(x.value) \u0026 (z.value)))\n\n\ndef H(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) | ~(y.value)) ^ (z.value))\n\n\ndef I(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (z.value)) | ((y.value) \u0026 ~(z.value)))\n\n\ndef J(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ ((y.value) | ~(z.value)))\n\n\n##### The ten basic operations FF() through III() #####\ndef FF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x5A827999).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6ED9EBA1).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef II(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x8F1BBCDC).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0xA953FD4E).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef FFF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef GGG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x7A6D76E9).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef HHH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6D703EF3).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef III(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x5C4DD124).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\ndef JJJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0x50A28BE6).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n\n# Initializes MDbuffer to \"magic constants\"\ndef MDinit(MDbuf: list[u32]):\n MDbuf[0] = c_uint32(0x67452301)\n MDbuf[1] = c_uint32(0xEFCDAB89)\n MDbuf[2] = c_uint32(0x98BADCFE)\n MDbuf[3] = c_uint32(0x10325476)\n MDbuf[4] = c_uint32(0xC3D2E1F0)\n\n\n##### The compression function. Transforms MDbuf using message bytes X[0] through X[15] #####\ndef compress(MDbuf: list[u32], X: list[u32]):\n aa = c_uint32(MDbuf[0].value)\n bb = c_uint32(MDbuf[1].value)\n cc = c_uint32(MDbuf[2].value)\n dd = c_uint32(MDbuf[3].value)\n ee = c_uint32(MDbuf[4].value)\n\n aaa = c_uint32(MDbuf[0].value)\n bbb = c_uint32(MDbuf[1].value)\n ccc = c_uint32(MDbuf[2].value)\n ddd = c_uint32(MDbuf[3].value)\n eee = c_uint32(MDbuf[4].value)\n\n # round 1\n FF(aa, bb, cc, dd, ee, X[0], c_uint32(11))\n FF(ee, aa, bb, cc, dd, X[1], c_uint32(14))\n FF(dd, ee, aa, bb, cc, X[2], c_uint32(15))\n FF(cc, dd, ee, aa, bb, X[3], c_uint32(12))\n FF(bb, cc, dd, ee, aa, X[4], c_uint32(5))\n FF(aa, bb, cc, dd, ee, X[5], c_uint32(8))\n FF(ee, aa, bb, cc, dd, X[6], c_uint32(7))\n FF(dd, ee, aa, bb, cc, X[7], c_uint32(9))\n FF(cc, dd, ee, aa, bb, X[8], c_uint32(11))\n FF(bb, cc, dd, ee, aa, X[9], c_uint32(13))\n FF(aa, bb, cc, dd, ee, X[10], c_uint32(14))\n FF(ee, aa, bb, cc, dd, X[11], c_uint32(15))\n FF(dd, ee, aa, bb, cc, X[12], c_uint32(6))\n FF(cc, dd, ee, aa, bb, X[13], c_uint32(7))\n FF(bb, cc, dd, ee, aa, X[14], c_uint32(9))\n FF(aa, bb, cc, dd, ee, X[15], c_uint32(8))\n\n # round 2\n GG(ee, aa, bb, cc, dd, X[7], c_uint32(7))\n GG(dd, ee, aa, bb, cc, X[4], c_uint32(6))\n GG(cc, dd, ee, aa, bb, X[13], c_uint32(8))\n GG(bb, cc, dd, ee, aa, X[1], c_uint32(13))\n GG(aa, bb, cc, dd, ee, X[10], c_uint32(11))\n GG(ee, aa, bb, cc, dd, X[6], c_uint32(9))\n GG(dd, ee, aa, bb, cc, X[15], c_uint32(7))\n GG(cc, dd, ee, aa, bb, X[3], c_uint32(15))\n GG(bb, cc, dd, ee, aa, X[12], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[0], c_uint32(12))\n GG(ee, aa, bb, cc, dd, X[9], c_uint32(15))\n GG(dd, ee, aa, bb, cc, X[5], c_uint32(9))\n GG(cc, dd, ee, aa, bb, X[2], c_uint32(11))\n GG(bb, cc, dd, ee, aa, X[14], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[11], c_uint32(13))\n GG(ee, aa, bb, cc, dd, X[8], c_uint32(12))\n\n # round 3\n HH(dd, ee, aa, bb, cc, X[3], c_uint32(11))\n HH(cc, dd, ee, aa, bb, X[10], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[14], c_uint32(6))\n HH(aa, bb, cc, dd, ee, X[4], c_uint32(7))\n HH(ee, aa, bb, cc, dd, X[9], c_uint32(14))\n HH(dd, ee, aa, bb, cc, X[15], c_uint32(9))\n HH(cc, dd, ee, aa, bb, X[8], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[1], c_uint32(15))\n HH(aa, bb, cc, dd, ee, X[2], c_uint32(14))\n HH(ee, aa, bb, cc, dd, X[7], c_uint32(8))\n HH(dd, ee, aa, bb, cc, X[0], c_uint32(13))\n HH(cc, dd, ee, aa, bb, X[6], c_uint32(6))\n HH(bb, cc, dd, ee, aa, X[13], c_uint32(5))\n HH(aa, bb, cc, dd, ee, X[11], c_uint32(12))\n HH(ee, aa, bb, cc, dd, X[5], c_uint32(7))\n HH(dd, ee, aa, bb, cc, X[12], c_uint32(5))\n\n # round 4\n II(cc, dd, ee, aa, bb, X[1], c_uint32(11))\n II(bb, cc, dd, ee, aa, X[9], c_uint32(12))\n II(aa, bb, cc, dd, ee, X[11], c_uint32(14))\n II(ee, aa, bb, cc, dd, X[10], c_uint32(15))\n II(dd, ee, aa, bb, cc, X[0], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[8], c_uint32(15))\n II(bb, cc, dd, ee, aa, X[12], c_uint32(9))\n II(aa, bb, cc, dd, ee, X[4], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[13], c_uint32(9))\n II(dd, ee, aa, bb, cc, X[3], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[7], c_uint32(5))\n II(bb, cc, dd, ee, aa, X[15], c_uint32(6))\n II(aa, bb, cc, dd, ee, X[14], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[5], c_uint32(6))\n II(dd, ee, aa, bb, cc, X[6], c_uint32(5))\n II(cc, dd, ee, aa, bb, X[2], c_uint32(12))\n\n # round 5\n JJ(bb, cc, dd, ee, aa, X[4], c_uint32(9))\n JJ(aa, bb, cc, dd, ee, X[0], c_uint32(15))\n JJ(ee, aa, bb, cc, dd, X[5], c_uint32(5))\n JJ(dd, ee, aa, bb, cc, X[9], c_uint32(11))\n JJ(cc, dd, ee, aa, bb, X[7], c_uint32(6))\n JJ(bb, cc, dd, ee, aa, X[12], c_uint32(8))\n JJ(aa, bb, cc, dd, ee, X[2], c_uint32(13))\n JJ(ee, aa, bb, cc, dd, X[10], c_uint32(12))\n JJ(dd, ee, aa, bb, cc, X[14], c_uint32(5))\n JJ(cc, dd, ee, aa, bb, X[1], c_uint32(12))\n JJ(bb, cc, dd, ee, aa, X[3], c_uint32(13))\n JJ(aa, bb, cc, dd, ee, X[8], c_uint32(14))\n JJ(ee, aa, bb, cc, dd, X[11], c_uint32(11))\n JJ(dd, ee, aa, bb, cc, X[6], c_uint32(8))\n JJ(cc, dd, ee, aa, bb, X[15], c_uint32(5))\n JJ(bb, cc, dd, ee, aa, X[13], c_uint32(6))\n\n # parallel round 1\n JJJ(aaa, bbb, ccc, ddd, eee, X[5], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[14], c_uint32(9))\n JJJ(ddd, eee, aaa, bbb, ccc, X[7], c_uint32(9))\n JJJ(ccc, ddd, eee, aaa, bbb, X[0], c_uint32(11))\n JJJ(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(13))\n JJJ(aaa, bbb, ccc, ddd, eee, X[2], c_uint32(15))\n JJJ(eee, aaa, bbb, ccc, ddd, X[11], c_uint32(15))\n JJJ(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(5))\n JJJ(ccc, ddd, eee, aaa, bbb, X[13], c_uint32(7))\n JJJ(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(7))\n JJJ(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(11))\n JJJ(ddd, eee, aaa, bbb, ccc, X[1], c_uint32(14))\n JJJ(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(14))\n JJJ(bbb, ccc, ddd, eee, aaa, X[3], c_uint32(12))\n JJJ(aaa, bbb, ccc, ddd, eee, X[12], c_uint32(6))\n\n # parallel round 2\n III(eee, aaa, bbb, ccc, ddd, X[6], c_uint32(9))\n III(ddd, eee, aaa, bbb, ccc, X[11], c_uint32(13))\n III(ccc, ddd, eee, aaa, bbb, X[3], c_uint32(15))\n III(bbb, ccc, ddd, eee, aaa, X[7], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(12))\n III(eee, aaa, bbb, ccc, ddd, X[13], c_uint32(8))\n III(ddd, eee, aaa, bbb, ccc, X[5], c_uint32(9))\n III(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(11))\n III(bbb, ccc, ddd, eee, aaa, X[14], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(7))\n III(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(12))\n III(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(7))\n III(ccc, ddd, eee, aaa, bbb, X[4], c_uint32(6))\n III(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(15))\n III(aaa, bbb, ccc, ddd, eee, X[1], c_uint32(13))\n III(eee, aaa, bbb, ccc, ddd, X[2], c_uint32(11))\n\n # parallel round 3\n HHH(ddd, eee, aaa, bbb, ccc, X[15], c_uint32(9))\n HHH(ccc, ddd, eee, aaa, bbb, X[5], c_uint32(7))\n HHH(bbb, ccc, ddd, eee, aaa, X[1], c_uint32(15))\n HHH(aaa, bbb, ccc, ddd, eee, X[3], c_uint32(11))\n HHH(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(8))\n HHH(ddd, eee, aaa, bbb, ccc, X[14], c_uint32(6))\n HHH(ccc, ddd, eee, aaa, bbb, X[6], c_uint32(6))\n HHH(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(14))\n HHH(aaa, bbb, ccc, ddd, eee, X[11], c_uint32(12))\n HHH(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(13))\n HHH(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(5))\n HHH(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(14))\n HHH(bbb, ccc, ddd, eee, aaa, X[10], c_uint32(13))\n HHH(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(13))\n HHH(eee, aaa, bbb, ccc, ddd, X[4], c_uint32(7))\n HHH(ddd, eee, aaa, bbb, ccc, X[13], c_uint32(5))\n\n # parallel round 4\n GGG(ccc, ddd, eee, aaa, bbb, X[8], c_uint32(15))\n GGG(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(5))\n GGG(aaa, bbb, ccc, ddd, eee, X[4], c_uint32(8))\n GGG(eee, aaa, bbb, ccc, ddd, X[1], c_uint32(11))\n GGG(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(14))\n GGG(ccc, ddd, eee, aaa, bbb, X[11], c_uint32(14))\n GGG(bbb, ccc, ddd, eee, aaa, X[15], c_uint32(6))\n GGG(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(14))\n GGG(eee, aaa, bbb, ccc, ddd, X[5], c_uint32(6))\n GGG(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(9))\n GGG(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(12))\n GGG(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(9))\n GGG(aaa, bbb, ccc, ddd, eee, X[9], c_uint32(12))\n GGG(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(5))\n GGG(ddd, eee, aaa, bbb, ccc, X[10], c_uint32(15))\n GGG(ccc, ddd, eee, aaa, bbb, X[14], c_uint32(8))\n\n # parallel round 5\n FFF(bbb, ccc, ddd, eee, aaa, X[12], c_uint32(8))\n FFF(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[10], c_uint32(12))\n FFF(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(9))\n FFF(ccc, ddd, eee, aaa, bbb, X[1], c_uint32(12))\n FFF(bbb, ccc, ddd, eee, aaa, X[5], c_uint32(5))\n FFF(aaa, bbb, ccc, ddd, eee, X[8], c_uint32(14))\n FFF(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(6))\n FFF(ddd, eee, aaa, bbb, ccc, X[6], c_uint32(8))\n FFF(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(13))\n FFF(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(6))\n FFF(aaa, bbb, ccc, ddd, eee, X[14], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[0], c_uint32(15))\n FFF(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(13))\n FFF(ccc, ddd, eee, aaa, bbb, X[9], c_uint32(11))\n FFF(bbb, ccc, ddd, eee, aaa, X[11], c_uint32(11))\n\n # combine results\n ddd.value += cc.value + MDbuf[1].value # final result for MDbuf[0]\n MDbuf[1].value = MDbuf[2].value + dd.value + eee.value\n MDbuf[2].value = MDbuf[3].value + ee.value + aaa.value\n MDbuf[3].value = MDbuf[4].value + aa.value + bbb.value\n MDbuf[4].value = MDbuf[0].value + bb.value + ccc.value\n MDbuf[0].value = ddd.value\n\n\n##### Puts bytes from strptr into X and pad out; appends length\n##### and finally, compresses the last block(s)\n##### note: length in bits == 8 * (lswlen + 2^32 mswlen).\n##### note: there are (lswlen mod 64) bytes left in strptr.\n\n\ndef MDfinish(MDbuf: list[u32], bytes: list[u8], lswlen: u32, mswlen: u32):\n i = c_uint32() # counter\n x = [c_uint32(0) for _ in range(16)]\n str_idx = 0\n\n # /* put bytes from strptr into X\n while i.value \u003C (lswlen.value \u0026 63):\n # byte i goes into word X[i div 4] at pos. 8*(i mod 4)\n x[i.value \u003E\u003E 2].value ^= (\n c_uint32(bytes[str_idx].value).value \u003C\u003C c_uint32((8 * (i.value \u0026 3))).value\n )\n i.value += 1\n str_idx += 1\n\n # append the bit m_n == 1\n x[(lswlen.value \u003E\u003E c_uint32(2).value) \u0026 15].value ^= c_uint32(1).value \u003C\u003C (\n 8 * (lswlen.value \u0026 3) + 7\n )\n\n if (lswlen.value \u0026 c_uint32(63).value) \u003E 55:\n compress(MDbuf, x)\n x = [c_uint32(0) for _ in range(16)]\n\n # append length in bits\n x[14].value = lswlen.value \u003C\u003C c_uint32(3).value\n x[15].value = (lswlen.value \u003E\u003E c_uint32(29).value) | (\n mswlen.value \u003C\u003C c_uint32(3).value\n )\n compress(MDbuf, x)\n\n\nRMDsize = 160\n\n\n# Converts 4 u8s into 1 u32\ndef u8_to_u32(strptr: list[u8]):\n return c_uint32(\n ((strptr[3]).value \u003C\u003C 24)\n | ((strptr[2]).value \u003C\u003C 16)\n | ((strptr[1]).value \u003C\u003C 8)\n | ((strptr[0].value))\n )\n\n\n##### Returns the RIPEMD-160 digest of the message in bytes #####\ndef RMD(message: list[u8]):\n MDbuf = [\n c_uint32(0) for _ in range((RMDsize / 32).__floor__())\n ] # contains (A, B, C, D, E)\n hashcode = [c_uint8(0) for _ in range((RMDsize / 8).__floor__())]\n # for final hash-value\n x = [c_uint32(0) for _ in range(16)] # current 16-word chunk\n i = c_uint32(0) # counter\n length = message.__len__() # length in bytes of message\n nbytes = message.__len__() # number of bytes not yet processed\n message_idx = 0\n\n MDinit(MDbuf)\n\n # process message in 16-word chunks\n\n while nbytes \u003E 63:\n i.value = 0\n while i.value \u003C 16:\n x[i.value] = u8_to_u32(message[message_idx:])\n message_idx += 4\n i.value += 1\n compress(MDbuf, x)\n nbytes -= 64\n\n # finish:\n MDfinish(MDbuf, message, c_uint32(length), c_uint32(0))\n i.value = 0\n while i.value \u003C RMDsize / 8:\n hashcode[i.value] = c_uint8(MDbuf[i.value \u003E\u003E 2].value)\n hashcode[i.value + 1] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 8)\n hashcode[i.value + 2] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 16)\n hashcode[i.value + 3] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 24)\n i.value += 4\n\n return hashcode\n\n\n##### Returns the RIPEMD-160 hash of the message in a string #####\ndef RMDstring(message: str):\n i = 0\n encoded_message: list[c_uint8] = [c_uint8(x) for x in list(message.encode())]\n\n hashcode = RMD(encoded_message)\n\n string = \"\"\n for i in hashcode:\n string += f\"{i:02x}\"\n return string\n\n\ndef Z10139(Z10139K1):\n i = 0\n encoded_message: list[c_uint8] = [c_uint8(x) for x in list(Z10139K1.encode())]\n\n hashcode = RMD(encoded_message)\n\n string = \"\"\n for i in hashcode:\n string += f\"{i:02x}\"\n return string\n"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Pure Python"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
6mmveecm3rj5pr09710cji607icbmr5
281625
281221
2026-06-07T20:39:35Z
Seller of unexistent friends
85698
281625
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "# Python translation of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.c\n# https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.h\n# and a bit of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/hashtest.c\n# It's not really pythonic but it's fine since it works.\n\ndef Z10139(Z10139K1):\n\tclass _FixedUint:\n\t _mask: int = 0\n\t\n\t def __init__(self, value: int = 0):\n\t self._value: int = value \u0026 self._mask\n\t\n\t @property\n\t def value(self) -\u003E int:\n\t return self._value\n\t\n\t @value.setter\n\t def value(self, v: int):\n\t self._value = v \u0026 self._mask\n\t\n\t def __format__(self, spec: str) -\u003E str:\n\t return format(self._value, spec)\n\t\n\t def __repr__(self) -\u003E str:\n\t return f\"{type(self).__name__}({self._value})\"\n\t\n\t\n\tclass c_uint8(_FixedUint):\n\t _mask: int = 0xFF\n\t\n\t\n\tclass c_uint32(_FixedUint):\n\t _mask: int = 0xFFFFFFFF\n\t\n\t\n\ttype u8 = c_uint8\n\ttype u32 = c_uint32\n\t\n\t\n\t# ROL(x, n) cyclically rotates x over n bits to the left\n\t# x must be of an unsigned 32 bits type and 0 \u003C= n \u003C 32.\n\tdef ROL(x: u32, n: u32):\n\t return c_uint32(((x.value) \u003C\u003C (n.value)) | ((x.value) \u003E\u003E (32 - (n.value))))\n\t\n\t\n\t##### The five basic functions F(), G() and H() #####\n\tdef F(x: u32, y: u32, z: u32):\n\t return c_uint32((x.value) ^ (y.value) ^ (z.value))\n\t\n\t\n\tdef G(x: u32, y: u32, z: u32):\n\t return c_uint32(((x.value) \u0026 (y.value)) | (~(x.value) \u0026 (z.value)))\n\t\n\t\n\tdef H(x: u32, y: u32, z: u32):\n\t return c_uint32(((x.value) | ~(y.value)) ^ (z.value))\n\t\n\t\n\tdef I(x: u32, y: u32, z: u32):\n\t return c_uint32(((x.value) \u0026 (z.value)) | ((y.value) \u0026 ~(z.value)))\n\t\n\t\n\tdef J(x: u32, y: u32, z: u32):\n\t return c_uint32((x.value) ^ ((y.value) | ~(z.value)))\n\t\n\t\n\t##### The ten basic operations FF() through III() #####\n\tdef FF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n\t (a).value += F((b), (c), (d)).value + (x).value\n\t (a).value = ROL((a), (s)).value + (e).value\n\t (c).value = ROL((c), c_uint32(10)).value\n\t\n\t\n\tdef GG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n\t (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x5A827999).value\n\t (a).value = ROL((a), (s)).value + (e).value\n\t (c).value = ROL((c), c_uint32(10)).value\n\t\n\t\n\tdef HH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n\t (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6ED9EBA1).value\n\t (a).value = ROL((a), (s)).value + (e).value\n\t (c).value = ROL((c), c_uint32(10)).value\n\t\n\t\n\tdef II(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n\t (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x8F1BBCDC).value\n\t (a).value = ROL((a), (s)).value + (e).value\n\t (c).value = ROL((c), c_uint32(10)).value\n\t\n\t\n\tdef JJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n\t (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0xA953FD4E).value\n\t (a).value = ROL((a), (s)).value + (e).value\n\t (c).value = ROL((c), c_uint32(10)).value\n\t\n\t\n\tdef FFF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n\t (a).value += F((b), (c), (d)).value + (x).value\n\t (a).value = ROL((a), (s)).value + (e).value\n\t (c).value = ROL((c), c_uint32(10)).value\n\t\n\t\n\tdef GGG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n\t (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x7A6D76E9).value\n\t (a).value = ROL((a), (s)).value + (e).value\n\t (c).value = ROL((c), c_uint32(10)).value\n\t\n\t\n\tdef HHH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n\t (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6D703EF3).value\n\t (a).value = ROL((a), (s)).value + (e).value\n\t (c).value = ROL((c), c_uint32(10)).value\n\t\n\t\n\tdef III(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n\t (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x5C4DD124).value\n\t (a).value = ROL((a), (s)).value + (e).value\n\t (c).value = ROL((c), c_uint32(10)).value\n\t\n\t\n\tdef JJJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n\t (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0x50A28BE6).value\n\t (a).value = ROL((a), (s)).value + (e).value\n\t (c).value = ROL((c), c_uint32(10)).value\n\t\n\t\n\t# Initializes MDbuffer to \"magic constants\"\n\tdef MDinit(MDbuf: list[u32]):\n\t MDbuf[0] = c_uint32(0x67452301)\n\t MDbuf[1] = c_uint32(0xEFCDAB89)\n\t MDbuf[2] = c_uint32(0x98BADCFE)\n\t MDbuf[3] = c_uint32(0x10325476)\n\t MDbuf[4] = c_uint32(0xC3D2E1F0)\n\t\n\t\n\t##### The compression function. Transforms MDbuf using message bytes X[0] through X[15] #####\n\tdef compress(MDbuf: list[u32], X: list[u32]):\n\t aa = c_uint32(MDbuf[0].value)\n\t bb = c_uint32(MDbuf[1].value)\n\t cc = c_uint32(MDbuf[2].value)\n\t dd = c_uint32(MDbuf[3].value)\n\t ee = c_uint32(MDbuf[4].value)\n\t\n\t aaa = c_uint32(MDbuf[0].value)\n\t bbb = c_uint32(MDbuf[1].value)\n\t ccc = c_uint32(MDbuf[2].value)\n\t ddd = c_uint32(MDbuf[3].value)\n\t eee = c_uint32(MDbuf[4].value)\n\t\n\t # round 1\n\t FF(aa, bb, cc, dd, ee, X[0], c_uint32(11))\n\t FF(ee, aa, bb, cc, dd, X[1], c_uint32(14))\n\t FF(dd, ee, aa, bb, cc, X[2], c_uint32(15))\n\t FF(cc, dd, ee, aa, bb, X[3], c_uint32(12))\n\t FF(bb, cc, dd, ee, aa, X[4], c_uint32(5))\n\t FF(aa, bb, cc, dd, ee, X[5], c_uint32(8))\n\t FF(ee, aa, bb, cc, dd, X[6], c_uint32(7))\n\t FF(dd, ee, aa, bb, cc, X[7], c_uint32(9))\n\t FF(cc, dd, ee, aa, bb, X[8], c_uint32(11))\n\t FF(bb, cc, dd, ee, aa, X[9], c_uint32(13))\n\t FF(aa, bb, cc, dd, ee, X[10], c_uint32(14))\n\t FF(ee, aa, bb, cc, dd, X[11], c_uint32(15))\n\t FF(dd, ee, aa, bb, cc, X[12], c_uint32(6))\n\t FF(cc, dd, ee, aa, bb, X[13], c_uint32(7))\n\t FF(bb, cc, dd, ee, aa, X[14], c_uint32(9))\n\t FF(aa, bb, cc, dd, ee, X[15], c_uint32(8))\n\t\n\t # round 2\n\t GG(ee, aa, bb, cc, dd, X[7], c_uint32(7))\n\t GG(dd, ee, aa, bb, cc, X[4], c_uint32(6))\n\t GG(cc, dd, ee, aa, bb, X[13], c_uint32(8))\n\t GG(bb, cc, dd, ee, aa, X[1], c_uint32(13))\n\t GG(aa, bb, cc, dd, ee, X[10], c_uint32(11))\n\t GG(ee, aa, bb, cc, dd, X[6], c_uint32(9))\n\t GG(dd, ee, aa, bb, cc, X[15], c_uint32(7))\n\t GG(cc, dd, ee, aa, bb, X[3], c_uint32(15))\n\t GG(bb, cc, dd, ee, aa, X[12], c_uint32(7))\n\t GG(aa, bb, cc, dd, ee, X[0], c_uint32(12))\n\t GG(ee, aa, bb, cc, dd, X[9], c_uint32(15))\n\t GG(dd, ee, aa, bb, cc, X[5], c_uint32(9))\n\t GG(cc, dd, ee, aa, bb, X[2], c_uint32(11))\n\t GG(bb, cc, dd, ee, aa, X[14], c_uint32(7))\n\t GG(aa, bb, cc, dd, ee, X[11], c_uint32(13))\n\t GG(ee, aa, bb, cc, dd, X[8], c_uint32(12))\n\t\n\t # round 3\n\t HH(dd, ee, aa, bb, cc, X[3], c_uint32(11))\n\t HH(cc, dd, ee, aa, bb, X[10], c_uint32(13))\n\t HH(bb, cc, dd, ee, aa, X[14], c_uint32(6))\n\t HH(aa, bb, cc, dd, ee, X[4], c_uint32(7))\n\t HH(ee, aa, bb, cc, dd, X[9], c_uint32(14))\n\t HH(dd, ee, aa, bb, cc, X[15], c_uint32(9))\n\t HH(cc, dd, ee, aa, bb, X[8], c_uint32(13))\n\t HH(bb, cc, dd, ee, aa, X[1], c_uint32(15))\n\t HH(aa, bb, cc, dd, ee, X[2], c_uint32(14))\n\t HH(ee, aa, bb, cc, dd, X[7], c_uint32(8))\n\t HH(dd, ee, aa, bb, cc, X[0], c_uint32(13))\n\t HH(cc, dd, ee, aa, bb, X[6], c_uint32(6))\n\t HH(bb, cc, dd, ee, aa, X[13], c_uint32(5))\n\t HH(aa, bb, cc, dd, ee, X[11], c_uint32(12))\n\t HH(ee, aa, bb, cc, dd, X[5], c_uint32(7))\n\t HH(dd, ee, aa, bb, cc, X[12], c_uint32(5))\n\t\n\t # round 4\n\t II(cc, dd, ee, aa, bb, X[1], c_uint32(11))\n\t II(bb, cc, dd, ee, aa, X[9], c_uint32(12))\n\t II(aa, bb, cc, dd, ee, X[11], c_uint32(14))\n\t II(ee, aa, bb, cc, dd, X[10], c_uint32(15))\n\t II(dd, ee, aa, bb, cc, X[0], c_uint32(14))\n\t II(cc, dd, ee, aa, bb, X[8], c_uint32(15))\n\t II(bb, cc, dd, ee, aa, X[12], c_uint32(9))\n\t II(aa, bb, cc, dd, ee, X[4], c_uint32(8))\n\t II(ee, aa, bb, cc, dd, X[13], c_uint32(9))\n\t II(dd, ee, aa, bb, cc, X[3], c_uint32(14))\n\t II(cc, dd, ee, aa, bb, X[7], c_uint32(5))\n\t II(bb, cc, dd, ee, aa, X[15], c_uint32(6))\n\t II(aa, bb, cc, dd, ee, X[14], c_uint32(8))\n\t II(ee, aa, bb, cc, dd, X[5], c_uint32(6))\n\t II(dd, ee, aa, bb, cc, X[6], c_uint32(5))\n\t II(cc, dd, ee, aa, bb, X[2], c_uint32(12))\n\t\n\t # round 5\n\t JJ(bb, cc, dd, ee, aa, X[4], c_uint32(9))\n\t JJ(aa, bb, cc, dd, ee, X[0], c_uint32(15))\n\t JJ(ee, aa, bb, cc, dd, X[5], c_uint32(5))\n\t JJ(dd, ee, aa, bb, cc, X[9], c_uint32(11))\n\t JJ(cc, dd, ee, aa, bb, X[7], c_uint32(6))\n\t JJ(bb, cc, dd, ee, aa, X[12], c_uint32(8))\n\t JJ(aa, bb, cc, dd, ee, X[2], c_uint32(13))\n\t JJ(ee, aa, bb, cc, dd, X[10], c_uint32(12))\n\t JJ(dd, ee, aa, bb, cc, X[14], c_uint32(5))\n\t JJ(cc, dd, ee, aa, bb, X[1], c_uint32(12))\n\t JJ(bb, cc, dd, ee, aa, X[3], c_uint32(13))\n\t JJ(aa, bb, cc, dd, ee, X[8], c_uint32(14))\n\t JJ(ee, aa, bb, cc, dd, X[11], c_uint32(11))\n\t JJ(dd, ee, aa, bb, cc, X[6], c_uint32(8))\n\t JJ(cc, dd, ee, aa, bb, X[15], c_uint32(5))\n\t JJ(bb, cc, dd, ee, aa, X[13], c_uint32(6))\n\t\n\t # parallel round 1\n\t JJJ(aaa, bbb, ccc, ddd, eee, X[5], c_uint32(8))\n\t JJJ(eee, aaa, bbb, ccc, ddd, X[14], c_uint32(9))\n\t JJJ(ddd, eee, aaa, bbb, ccc, X[7], c_uint32(9))\n\t JJJ(ccc, ddd, eee, aaa, bbb, X[0], c_uint32(11))\n\t JJJ(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(13))\n\t JJJ(aaa, bbb, ccc, ddd, eee, X[2], c_uint32(15))\n\t JJJ(eee, aaa, bbb, ccc, ddd, X[11], c_uint32(15))\n\t JJJ(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(5))\n\t JJJ(ccc, ddd, eee, aaa, bbb, X[13], c_uint32(7))\n\t JJJ(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(7))\n\t JJJ(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(8))\n\t JJJ(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(11))\n\t JJJ(ddd, eee, aaa, bbb, ccc, X[1], c_uint32(14))\n\t JJJ(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(14))\n\t JJJ(bbb, ccc, ddd, eee, aaa, X[3], c_uint32(12))\n\t JJJ(aaa, bbb, ccc, ddd, eee, X[12], c_uint32(6))\n\t\n\t # parallel round 2\n\t III(eee, aaa, bbb, ccc, ddd, X[6], c_uint32(9))\n\t III(ddd, eee, aaa, bbb, ccc, X[11], c_uint32(13))\n\t III(ccc, ddd, eee, aaa, bbb, X[3], c_uint32(15))\n\t III(bbb, ccc, ddd, eee, aaa, X[7], c_uint32(7))\n\t III(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(12))\n\t III(eee, aaa, bbb, ccc, ddd, X[13], c_uint32(8))\n\t III(ddd, eee, aaa, bbb, ccc, X[5], c_uint32(9))\n\t III(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(11))\n\t III(bbb, ccc, ddd, eee, aaa, X[14], c_uint32(7))\n\t III(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(7))\n\t III(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(12))\n\t III(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(7))\n\t III(ccc, ddd, eee, aaa, bbb, X[4], c_uint32(6))\n\t III(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(15))\n\t III(aaa, bbb, ccc, ddd, eee, X[1], c_uint32(13))\n\t III(eee, aaa, bbb, ccc, ddd, X[2], c_uint32(11))\n\t\n\t # parallel round 3\n\t HHH(ddd, eee, aaa, bbb, ccc, X[15], c_uint32(9))\n\t HHH(ccc, ddd, eee, aaa, bbb, X[5], c_uint32(7))\n\t HHH(bbb, ccc, ddd, eee, aaa, X[1], c_uint32(15))\n\t HHH(aaa, bbb, ccc, ddd, eee, X[3], c_uint32(11))\n\t HHH(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(8))\n\t HHH(ddd, eee, aaa, bbb, ccc, X[14], c_uint32(6))\n\t HHH(ccc, ddd, eee, aaa, bbb, X[6], c_uint32(6))\n\t HHH(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(14))\n\t HHH(aaa, bbb, ccc, ddd, eee, X[11], c_uint32(12))\n\t HHH(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(13))\n\t HHH(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(5))\n\t HHH(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(14))\n\t HHH(bbb, ccc, ddd, eee, aaa, X[10], c_uint32(13))\n\t HHH(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(13))\n\t HHH(eee, aaa, bbb, ccc, ddd, X[4], c_uint32(7))\n\t HHH(ddd, eee, aaa, bbb, ccc, X[13], c_uint32(5))\n\t\n\t # parallel round 4\n\t GGG(ccc, ddd, eee, aaa, bbb, X[8], c_uint32(15))\n\t GGG(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(5))\n\t GGG(aaa, bbb, ccc, ddd, eee, X[4], c_uint32(8))\n\t GGG(eee, aaa, bbb, ccc, ddd, X[1], c_uint32(11))\n\t GGG(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(14))\n\t GGG(ccc, ddd, eee, aaa, bbb, X[11], c_uint32(14))\n\t GGG(bbb, ccc, ddd, eee, aaa, X[15], c_uint32(6))\n\t GGG(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(14))\n\t GGG(eee, aaa, bbb, ccc, ddd, X[5], c_uint32(6))\n\t GGG(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(9))\n\t GGG(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(12))\n\t GGG(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(9))\n\t GGG(aaa, bbb, ccc, ddd, eee, X[9], c_uint32(12))\n\t GGG(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(5))\n\t GGG(ddd, eee, aaa, bbb, ccc, X[10], c_uint32(15))\n\t GGG(ccc, ddd, eee, aaa, bbb, X[14], c_uint32(8))\n\t\n\t # parallel round 5\n\t FFF(bbb, ccc, ddd, eee, aaa, X[12], c_uint32(8))\n\t FFF(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(5))\n\t FFF(eee, aaa, bbb, ccc, ddd, X[10], c_uint32(12))\n\t FFF(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(9))\n\t FFF(ccc, ddd, eee, aaa, bbb, X[1], c_uint32(12))\n\t FFF(bbb, ccc, ddd, eee, aaa, X[5], c_uint32(5))\n\t FFF(aaa, bbb, ccc, ddd, eee, X[8], c_uint32(14))\n\t FFF(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(6))\n\t FFF(ddd, eee, aaa, bbb, ccc, X[6], c_uint32(8))\n\t FFF(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(13))\n\t FFF(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(6))\n\t FFF(aaa, bbb, ccc, ddd, eee, X[14], c_uint32(5))\n\t FFF(eee, aaa, bbb, ccc, ddd, X[0], c_uint32(15))\n\t FFF(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(13))\n\t FFF(ccc, ddd, eee, aaa, bbb, X[9], c_uint32(11))\n\t FFF(bbb, ccc, ddd, eee, aaa, X[11], c_uint32(11))\n\t\n\t # combine results\n\t ddd.value += cc.value + MDbuf[1].value # final result for MDbuf[0]\n\t MDbuf[1].value = MDbuf[2].value + dd.value + eee.value\n\t MDbuf[2].value = MDbuf[3].value + ee.value + aaa.value\n\t MDbuf[3].value = MDbuf[4].value + aa.value + bbb.value\n\t MDbuf[4].value = MDbuf[0].value + bb.value + ccc.value\n\t MDbuf[0].value = ddd.value\n\t\n\t\n\t##### Puts bytes from strptr into X and pad out; appends length\n\t##### and finally, compresses the last block(s)\n\t##### note: length in bits == 8 * (lswlen + 2^32 mswlen).\n\t##### note: there are (lswlen mod 64) bytes left in strptr.\n\t\n\t\n\tdef MDfinish(MDbuf: list[u32], bytes: list[u8], lswlen: u32, mswlen: u32):\n\t i = c_uint32() # counter\n\t x = [c_uint32(0) for _ in range(16)]\n\t str_idx = 0\n\t\n\t # /* put bytes from strptr into X\n\t while i.value \u003C (lswlen.value \u0026 63):\n\t # byte i goes into word X[i div 4] at pos. 8*(i mod 4)\n\t x[i.value \u003E\u003E 2].value ^= (\n\t c_uint32(bytes[str_idx].value).value \u003C\u003C c_uint32((8 * (i.value \u0026 3))).value\n\t )\n\t i.value += 1\n\t str_idx += 1\n\t\n\t # append the bit m_n == 1\n\t x[(lswlen.value \u003E\u003E c_uint32(2).value) \u0026 15].value ^= c_uint32(1).value \u003C\u003C (\n\t 8 * (lswlen.value \u0026 3) + 7\n\t )\n\t\n\t if (lswlen.value \u0026 c_uint32(63).value) \u003E 55:\n\t compress(MDbuf, x)\n\t x = [c_uint32(0) for _ in range(16)]\n\t\n\t # append length in bits\n\t x[14].value = lswlen.value \u003C\u003C c_uint32(3).value\n\t x[15].value = (lswlen.value \u003E\u003E c_uint32(29).value) | (\n\t mswlen.value \u003C\u003C c_uint32(3).value\n\t )\n\t compress(MDbuf, x)\n\t\n\t\n\tRMDsize = 160\n\t\n\t\n\t# Converts 4 u8s into 1 u32\n\tdef u8_to_u32(strptr: list[u8]):\n\t return c_uint32(\n\t ((strptr[3]).value \u003C\u003C 24)\n\t | ((strptr[2]).value \u003C\u003C 16)\n\t | ((strptr[1]).value \u003C\u003C 8)\n\t | ((strptr[0].value))\n\t )\n\t\n\t\n\t##### Returns the RIPEMD-160 digest of the message in bytes #####\n\tdef RMD(message: list[u8]):\n\t MDbuf = [\n\t c_uint32(0) for _ in range((RMDsize / 32).__floor__())\n\t ] # contains (A, B, C, D, E)\n\t hashcode = [c_uint8(0) for _ in range((RMDsize / 8).__floor__())]\n\t # for final hash-value\n\t x = [c_uint32(0) for _ in range(16)] # current 16-word chunk\n\t i = c_uint32(0) # counter\n\t length = message.__len__() # length in bytes of message\n\t nbytes = message.__len__() # number of bytes not yet processed\n\t message_idx = 0\n\t\n\t MDinit(MDbuf)\n\t\n\t # process message in 16-word chunks\n\t\n\t while nbytes \u003E 63:\n\t i.value = 0\n\t while i.value \u003C 16:\n\t x[i.value] = u8_to_u32(message[message_idx:])\n\t message_idx += 4\n\t i.value += 1\n\t compress(MDbuf, x)\n\t nbytes -= 64\n\t\n\t # finish:\n\t MDfinish(MDbuf, message, c_uint32(length), c_uint32(0))\n\t i.value = 0\n\t while i.value \u003C RMDsize / 8:\n\t hashcode[i.value] = c_uint8(MDbuf[i.value \u003E\u003E 2].value)\n\t hashcode[i.value + 1] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 8)\n\t hashcode[i.value + 2] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 16)\n\t hashcode[i.value + 3] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 24)\n\t i.value += 4\n\t\n\t return hashcode\n\t\n\n i = 0\n encoded_message: list[c_uint8] = [c_uint8(x) for x in list(Z10139K1.encode())]\n\n hashcode = RMD(encoded_message)\n\n string = \"\"\n for i in hashcode:\n string += f\"{i:02x}\"\n return string\n"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Pure Python"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
9f82hiylsaudqalo35n3mh7x8vzqbyr
281627
281625
2026-06-07T20:43:08Z
Seller of unexistent friends
85698
281627
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "# Python translation of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.c\n# https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.h\n# and a bit of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/hashtest.c\n# It's not really pythonic but it's fine since it works.\n\n\ndef Z10139(Z10139K1: str):\n\t# For some reason functions and classes defined outside of the function that wikifunctions calls don't work so which is why everything's here.\n class _FixedUint:\n _mask: int = 0\n\n def __init__(self, value: int = 0):\n self._value: int = value \u0026 self._mask\n\n @property\n def value(self) -\u003E int:\n return self._value\n\n @value.setter\n def value(self, v: int):\n self._value = v \u0026 self._mask\n\n def __format__(self, spec: str) -\u003E str:\n return format(self._value, spec)\n\n def __repr__(self) -\u003E str:\n return f\"{type(self).__name__}({self._value})\"\n\n class c_uint8(_FixedUint):\n _mask: int = 0xFF\n\n class c_uint32(_FixedUint):\n _mask: int = 0xFFFFFFFF\n\n type u8 = c_uint8\n type u32 = c_uint32\n\n # ROL(x, n) cyclically rotates x over n bits to the left\n # x must be of an unsigned 32 bits type and 0 \u003C= n \u003C 32.\n def ROL(x: u32, n: u32):\n return c_uint32(((x.value) \u003C\u003C (n.value)) | ((x.value) \u003E\u003E (32 - (n.value))))\n\n ##### The five basic functions F(), G() and H() #####\n def F(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ (y.value) ^ (z.value))\n\n def G(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (y.value)) | (~(x.value) \u0026 (z.value)))\n\n def H(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) | ~(y.value)) ^ (z.value))\n\n def I(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (z.value)) | ((y.value) \u0026 ~(z.value)))\n\n def J(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ ((y.value) | ~(z.value)))\n\n ##### The ten basic operations FF() through III() #####\n def FF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def GG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x5A827999).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def HH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6ED9EBA1).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def II(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x8F1BBCDC).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def JJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0xA953FD4E).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def FFF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def GGG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x7A6D76E9).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def HHH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6D703EF3).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def III(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x5C4DD124).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def JJJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0x50A28BE6).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n # Initializes MDbuffer to \"magic constants\"\n def MDinit(MDbuf: list[u32]):\n MDbuf[0] = c_uint32(0x67452301)\n MDbuf[1] = c_uint32(0xEFCDAB89)\n MDbuf[2] = c_uint32(0x98BADCFE)\n MDbuf[3] = c_uint32(0x10325476)\n MDbuf[4] = c_uint32(0xC3D2E1F0)\n\n ##### The compression function. Transforms MDbuf using message bytes X[0] through X[15] #####\n def compress(MDbuf: list[u32], X: list[u32]):\n aa = c_uint32(MDbuf[0].value)\n bb = c_uint32(MDbuf[1].value)\n cc = c_uint32(MDbuf[2].value)\n dd = c_uint32(MDbuf[3].value)\n ee = c_uint32(MDbuf[4].value)\n\n aaa = c_uint32(MDbuf[0].value)\n bbb = c_uint32(MDbuf[1].value)\n ccc = c_uint32(MDbuf[2].value)\n ddd = c_uint32(MDbuf[3].value)\n eee = c_uint32(MDbuf[4].value)\n\n # round 1\n FF(aa, bb, cc, dd, ee, X[0], c_uint32(11))\n FF(ee, aa, bb, cc, dd, X[1], c_uint32(14))\n FF(dd, ee, aa, bb, cc, X[2], c_uint32(15))\n FF(cc, dd, ee, aa, bb, X[3], c_uint32(12))\n FF(bb, cc, dd, ee, aa, X[4], c_uint32(5))\n FF(aa, bb, cc, dd, ee, X[5], c_uint32(8))\n FF(ee, aa, bb, cc, dd, X[6], c_uint32(7))\n FF(dd, ee, aa, bb, cc, X[7], c_uint32(9))\n FF(cc, dd, ee, aa, bb, X[8], c_uint32(11))\n FF(bb, cc, dd, ee, aa, X[9], c_uint32(13))\n FF(aa, bb, cc, dd, ee, X[10], c_uint32(14))\n FF(ee, aa, bb, cc, dd, X[11], c_uint32(15))\n FF(dd, ee, aa, bb, cc, X[12], c_uint32(6))\n FF(cc, dd, ee, aa, bb, X[13], c_uint32(7))\n FF(bb, cc, dd, ee, aa, X[14], c_uint32(9))\n FF(aa, bb, cc, dd, ee, X[15], c_uint32(8))\n\n # round 2\n GG(ee, aa, bb, cc, dd, X[7], c_uint32(7))\n GG(dd, ee, aa, bb, cc, X[4], c_uint32(6))\n GG(cc, dd, ee, aa, bb, X[13], c_uint32(8))\n GG(bb, cc, dd, ee, aa, X[1], c_uint32(13))\n GG(aa, bb, cc, dd, ee, X[10], c_uint32(11))\n GG(ee, aa, bb, cc, dd, X[6], c_uint32(9))\n GG(dd, ee, aa, bb, cc, X[15], c_uint32(7))\n GG(cc, dd, ee, aa, bb, X[3], c_uint32(15))\n GG(bb, cc, dd, ee, aa, X[12], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[0], c_uint32(12))\n GG(ee, aa, bb, cc, dd, X[9], c_uint32(15))\n GG(dd, ee, aa, bb, cc, X[5], c_uint32(9))\n GG(cc, dd, ee, aa, bb, X[2], c_uint32(11))\n GG(bb, cc, dd, ee, aa, X[14], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[11], c_uint32(13))\n GG(ee, aa, bb, cc, dd, X[8], c_uint32(12))\n\n # round 3\n HH(dd, ee, aa, bb, cc, X[3], c_uint32(11))\n HH(cc, dd, ee, aa, bb, X[10], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[14], c_uint32(6))\n HH(aa, bb, cc, dd, ee, X[4], c_uint32(7))\n HH(ee, aa, bb, cc, dd, X[9], c_uint32(14))\n HH(dd, ee, aa, bb, cc, X[15], c_uint32(9))\n HH(cc, dd, ee, aa, bb, X[8], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[1], c_uint32(15))\n HH(aa, bb, cc, dd, ee, X[2], c_uint32(14))\n HH(ee, aa, bb, cc, dd, X[7], c_uint32(8))\n HH(dd, ee, aa, bb, cc, X[0], c_uint32(13))\n HH(cc, dd, ee, aa, bb, X[6], c_uint32(6))\n HH(bb, cc, dd, ee, aa, X[13], c_uint32(5))\n HH(aa, bb, cc, dd, ee, X[11], c_uint32(12))\n HH(ee, aa, bb, cc, dd, X[5], c_uint32(7))\n HH(dd, ee, aa, bb, cc, X[12], c_uint32(5))\n\n # round 4\n II(cc, dd, ee, aa, bb, X[1], c_uint32(11))\n II(bb, cc, dd, ee, aa, X[9], c_uint32(12))\n II(aa, bb, cc, dd, ee, X[11], c_uint32(14))\n II(ee, aa, bb, cc, dd, X[10], c_uint32(15))\n II(dd, ee, aa, bb, cc, X[0], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[8], c_uint32(15))\n II(bb, cc, dd, ee, aa, X[12], c_uint32(9))\n II(aa, bb, cc, dd, ee, X[4], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[13], c_uint32(9))\n II(dd, ee, aa, bb, cc, X[3], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[7], c_uint32(5))\n II(bb, cc, dd, ee, aa, X[15], c_uint32(6))\n II(aa, bb, cc, dd, ee, X[14], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[5], c_uint32(6))\n II(dd, ee, aa, bb, cc, X[6], c_uint32(5))\n II(cc, dd, ee, aa, bb, X[2], c_uint32(12))\n\n # round 5\n JJ(bb, cc, dd, ee, aa, X[4], c_uint32(9))\n JJ(aa, bb, cc, dd, ee, X[0], c_uint32(15))\n JJ(ee, aa, bb, cc, dd, X[5], c_uint32(5))\n JJ(dd, ee, aa, bb, cc, X[9], c_uint32(11))\n JJ(cc, dd, ee, aa, bb, X[7], c_uint32(6))\n JJ(bb, cc, dd, ee, aa, X[12], c_uint32(8))\n JJ(aa, bb, cc, dd, ee, X[2], c_uint32(13))\n JJ(ee, aa, bb, cc, dd, X[10], c_uint32(12))\n JJ(dd, ee, aa, bb, cc, X[14], c_uint32(5))\n JJ(cc, dd, ee, aa, bb, X[1], c_uint32(12))\n JJ(bb, cc, dd, ee, aa, X[3], c_uint32(13))\n JJ(aa, bb, cc, dd, ee, X[8], c_uint32(14))\n JJ(ee, aa, bb, cc, dd, X[11], c_uint32(11))\n JJ(dd, ee, aa, bb, cc, X[6], c_uint32(8))\n JJ(cc, dd, ee, aa, bb, X[15], c_uint32(5))\n JJ(bb, cc, dd, ee, aa, X[13], c_uint32(6))\n\n # parallel round 1\n JJJ(aaa, bbb, ccc, ddd, eee, X[5], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[14], c_uint32(9))\n JJJ(ddd, eee, aaa, bbb, ccc, X[7], c_uint32(9))\n JJJ(ccc, ddd, eee, aaa, bbb, X[0], c_uint32(11))\n JJJ(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(13))\n JJJ(aaa, bbb, ccc, ddd, eee, X[2], c_uint32(15))\n JJJ(eee, aaa, bbb, ccc, ddd, X[11], c_uint32(15))\n JJJ(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(5))\n JJJ(ccc, ddd, eee, aaa, bbb, X[13], c_uint32(7))\n JJJ(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(7))\n JJJ(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(11))\n JJJ(ddd, eee, aaa, bbb, ccc, X[1], c_uint32(14))\n JJJ(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(14))\n JJJ(bbb, ccc, ddd, eee, aaa, X[3], c_uint32(12))\n JJJ(aaa, bbb, ccc, ddd, eee, X[12], c_uint32(6))\n\n # parallel round 2\n III(eee, aaa, bbb, ccc, ddd, X[6], c_uint32(9))\n III(ddd, eee, aaa, bbb, ccc, X[11], c_uint32(13))\n III(ccc, ddd, eee, aaa, bbb, X[3], c_uint32(15))\n III(bbb, ccc, ddd, eee, aaa, X[7], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(12))\n III(eee, aaa, bbb, ccc, ddd, X[13], c_uint32(8))\n III(ddd, eee, aaa, bbb, ccc, X[5], c_uint32(9))\n III(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(11))\n III(bbb, ccc, ddd, eee, aaa, X[14], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(7))\n III(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(12))\n III(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(7))\n III(ccc, ddd, eee, aaa, bbb, X[4], c_uint32(6))\n III(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(15))\n III(aaa, bbb, ccc, ddd, eee, X[1], c_uint32(13))\n III(eee, aaa, bbb, ccc, ddd, X[2], c_uint32(11))\n\n # parallel round 3\n HHH(ddd, eee, aaa, bbb, ccc, X[15], c_uint32(9))\n HHH(ccc, ddd, eee, aaa, bbb, X[5], c_uint32(7))\n HHH(bbb, ccc, ddd, eee, aaa, X[1], c_uint32(15))\n HHH(aaa, bbb, ccc, ddd, eee, X[3], c_uint32(11))\n HHH(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(8))\n HHH(ddd, eee, aaa, bbb, ccc, X[14], c_uint32(6))\n HHH(ccc, ddd, eee, aaa, bbb, X[6], c_uint32(6))\n HHH(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(14))\n HHH(aaa, bbb, ccc, ddd, eee, X[11], c_uint32(12))\n HHH(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(13))\n HHH(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(5))\n HHH(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(14))\n HHH(bbb, ccc, ddd, eee, aaa, X[10], c_uint32(13))\n HHH(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(13))\n HHH(eee, aaa, bbb, ccc, ddd, X[4], c_uint32(7))\n HHH(ddd, eee, aaa, bbb, ccc, X[13], c_uint32(5))\n\n # parallel round 4\n GGG(ccc, ddd, eee, aaa, bbb, X[8], c_uint32(15))\n GGG(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(5))\n GGG(aaa, bbb, ccc, ddd, eee, X[4], c_uint32(8))\n GGG(eee, aaa, bbb, ccc, ddd, X[1], c_uint32(11))\n GGG(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(14))\n GGG(ccc, ddd, eee, aaa, bbb, X[11], c_uint32(14))\n GGG(bbb, ccc, ddd, eee, aaa, X[15], c_uint32(6))\n GGG(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(14))\n GGG(eee, aaa, bbb, ccc, ddd, X[5], c_uint32(6))\n GGG(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(9))\n GGG(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(12))\n GGG(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(9))\n GGG(aaa, bbb, ccc, ddd, eee, X[9], c_uint32(12))\n GGG(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(5))\n GGG(ddd, eee, aaa, bbb, ccc, X[10], c_uint32(15))\n GGG(ccc, ddd, eee, aaa, bbb, X[14], c_uint32(8))\n\n # parallel round 5\n FFF(bbb, ccc, ddd, eee, aaa, X[12], c_uint32(8))\n FFF(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[10], c_uint32(12))\n FFF(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(9))\n FFF(ccc, ddd, eee, aaa, bbb, X[1], c_uint32(12))\n FFF(bbb, ccc, ddd, eee, aaa, X[5], c_uint32(5))\n FFF(aaa, bbb, ccc, ddd, eee, X[8], c_uint32(14))\n FFF(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(6))\n FFF(ddd, eee, aaa, bbb, ccc, X[6], c_uint32(8))\n FFF(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(13))\n FFF(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(6))\n FFF(aaa, bbb, ccc, ddd, eee, X[14], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[0], c_uint32(15))\n FFF(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(13))\n FFF(ccc, ddd, eee, aaa, bbb, X[9], c_uint32(11))\n FFF(bbb, ccc, ddd, eee, aaa, X[11], c_uint32(11))\n\n # combine results\n ddd.value += cc.value + MDbuf[1].value # final result for MDbuf[0]\n MDbuf[1].value = MDbuf[2].value + dd.value + eee.value\n MDbuf[2].value = MDbuf[3].value + ee.value + aaa.value\n MDbuf[3].value = MDbuf[4].value + aa.value + bbb.value\n MDbuf[4].value = MDbuf[0].value + bb.value + ccc.value\n MDbuf[0].value = ddd.value\n\n ##### Puts bytes from strptr into X and pad out; appends length\n ##### and finally, compresses the last block(s)\n ##### note: length in bits == 8 * (lswlen + 2^32 mswlen).\n ##### note: there are (lswlen mod 64) bytes left in strptr.\n\n def MDfinish(MDbuf: list[u32], bytes: list[u8], lswlen: u32, mswlen: u32):\n i = c_uint32() # counter\n x = [c_uint32(0) for _ in range(16)]\n str_idx = 0\n\n # /* put bytes from strptr into X\n while i.value \u003C (lswlen.value \u0026 63):\n # byte i goes into word X[i div 4] at pos. 8*(i mod 4)\n x[i.value \u003E\u003E 2].value ^= (\n c_uint32(bytes[str_idx].value).value\n \u003C\u003C c_uint32((8 * (i.value \u0026 3))).value\n )\n i.value += 1\n str_idx += 1\n\n # append the bit m_n == 1\n x[(lswlen.value \u003E\u003E c_uint32(2).value) \u0026 15].value ^= c_uint32(1).value \u003C\u003C (\n 8 * (lswlen.value \u0026 3) + 7\n )\n\n if (lswlen.value \u0026 c_uint32(63).value) \u003E 55:\n compress(MDbuf, x)\n x = [c_uint32(0) for _ in range(16)]\n\n # append length in bits\n x[14].value = lswlen.value \u003C\u003C c_uint32(3).value\n x[15].value = (lswlen.value \u003E\u003E c_uint32(29).value) | (\n mswlen.value \u003C\u003C c_uint32(3).value\n )\n compress(MDbuf, x)\n\n RMDsize = 160\n\n # Converts 4 u8s into 1 u32\n def u8_to_u32(strptr: list[u8]):\n return c_uint32(\n ((strptr[3]).value \u003C\u003C 24)\n | ((strptr[2]).value \u003C\u003C 16)\n | ((strptr[1]).value \u003C\u003C 8)\n | ((strptr[0].value))\n )\n\n ##### Returns the RIPEMD-160 digest of the message in bytes #####\n def RMD(message: list[u8]):\n MDbuf = [\n c_uint32(0) for _ in range((RMDsize / 32).__floor__())\n ] # contains (A, B, C, D, E)\n hashcode = [c_uint8(0) for _ in range((RMDsize / 8).__floor__())]\n # for final hash-value\n x = [c_uint32(0) for _ in range(16)] # current 16-word chunk\n i = c_uint32(0) # counter\n length = message.__len__() # length in bytes of message\n nbytes = message.__len__() # number of bytes not yet processed\n message_idx = 0\n\n MDinit(MDbuf)\n\n # process message in 16-word chunks\n\n while nbytes \u003E 63:\n i.value = 0\n while i.value \u003C 16:\n x[i.value] = u8_to_u32(message[message_idx:])\n message_idx += 4\n i.value += 1\n compress(MDbuf, x)\n nbytes -= 64\n\n # finish:\n MDfinish(MDbuf, message, c_uint32(length), c_uint32(0))\n i.value = 0\n while i.value \u003C RMDsize / 8:\n hashcode[i.value] = c_uint8(MDbuf[i.value \u003E\u003E 2].value)\n hashcode[i.value + 1] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 8)\n hashcode[i.value + 2] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 16)\n hashcode[i.value + 3] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 24)\n i.value += 4\n\n return hashcode\n\n ##### Returns the RIPEMD-160 hash of the message in a string #####\n def RMDstring(message: str):\n i = 0\n encoded_message: list[c_uint8] = [c_uint8(x) for x in list(message.encode())]\n\n hashcode = RMD(encoded_message)\n\n string = \"\"\n for i in hashcode:\n string += f\"{i:02x}\"\n return string\n\n return RMDstring(Z10139K1)\n"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Pure Python"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
rwewz1hacwwwhtqw6ii1pouyby5xdpc
281628
281627
2026-06-07T20:44:39Z
Seller of unexistent friends
85698
281628
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36171"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z10139",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z610",
"Z16K2": "# Python translation of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.c\n# https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/rmd160.h\n# and a bit of https://homes.esat.kuleuven.be/~bosselae/ripemd160/ps/AB-9601/hashtest.c\n# It's not really pythonic but it's fine since it works.\n\n\ndef Z10139(Z10139K1: str):\n\t# For some reason functions and classes defined outside of the function that wikifunctions calls don't work so which is why everything's here.\n class _FixedUint:\n _mask: int = 0\n\n def __init__(self, value: int = 0):\n self._value: int = value \u0026 self._mask\n\n @property\n def value(self) -\u003E int:\n return self._value\n\n @value.setter\n def value(self, v: int):\n self._value = v \u0026 self._mask\n\n def __format__(self, spec: str) -\u003E str:\n return format(self._value, spec)\n\n def __repr__(self) -\u003E str:\n return f\"{type(self).__name__}({self._value})\"\n\n class c_uint8(_FixedUint):\n _mask: int = 0xFF\n\n class c_uint32(_FixedUint):\n _mask: int = 0xFFFFFFFF\n # pyright complains about this (A type statement can be used only within a module or class scope) but it's fine since it runs anyway and it wouldn't be an issue if I could have things outside of the function.\n type u8 = c_uint8\n type u32 = c_uint32\n\n # ROL(x, n) cyclically rotates x over n bits to the left\n # x must be of an unsigned 32 bits type and 0 \u003C= n \u003C 32.\n def ROL(x: u32, n: u32):\n return c_uint32(((x.value) \u003C\u003C (n.value)) | ((x.value) \u003E\u003E (32 - (n.value))))\n\n ##### The five basic functions F(), G() and H() #####\n def F(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ (y.value) ^ (z.value))\n\n def G(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (y.value)) | (~(x.value) \u0026 (z.value)))\n\n def H(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) | ~(y.value)) ^ (z.value))\n\n def I(x: u32, y: u32, z: u32):\n return c_uint32(((x.value) \u0026 (z.value)) | ((y.value) \u0026 ~(z.value)))\n\n def J(x: u32, y: u32, z: u32):\n return c_uint32((x.value) ^ ((y.value) | ~(z.value)))\n\n ##### The ten basic operations FF() through III() #####\n def FF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def GG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x5A827999).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def HH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6ED9EBA1).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def II(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x8F1BBCDC).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def JJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0xA953FD4E).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def FFF(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += F((b), (c), (d)).value + (x).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def GGG(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += G((b), (c), (d)).value + (x).value + c_uint32(0x7A6D76E9).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def HHH(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += H((b), (c), (d)).value + (x).value + c_uint32(0x6D703EF3).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def III(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += I((b), (c), (d)).value + (x).value + c_uint32(0x5C4DD124).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n def JJJ(a: u32, b: u32, c: u32, d: u32, e: u32, x: u32, s: u32):\n (a).value += J((b), (c), (d)).value + (x).value + c_uint32(0x50A28BE6).value\n (a).value = ROL((a), (s)).value + (e).value\n (c).value = ROL((c), c_uint32(10)).value\n\n # Initializes MDbuffer to \"magic constants\"\n def MDinit(MDbuf: list[u32]):\n MDbuf[0] = c_uint32(0x67452301)\n MDbuf[1] = c_uint32(0xEFCDAB89)\n MDbuf[2] = c_uint32(0x98BADCFE)\n MDbuf[3] = c_uint32(0x10325476)\n MDbuf[4] = c_uint32(0xC3D2E1F0)\n\n ##### The compression function. Transforms MDbuf using message bytes X[0] through X[15] #####\n def compress(MDbuf: list[u32], X: list[u32]):\n aa = c_uint32(MDbuf[0].value)\n bb = c_uint32(MDbuf[1].value)\n cc = c_uint32(MDbuf[2].value)\n dd = c_uint32(MDbuf[3].value)\n ee = c_uint32(MDbuf[4].value)\n\n aaa = c_uint32(MDbuf[0].value)\n bbb = c_uint32(MDbuf[1].value)\n ccc = c_uint32(MDbuf[2].value)\n ddd = c_uint32(MDbuf[3].value)\n eee = c_uint32(MDbuf[4].value)\n\n # round 1\n FF(aa, bb, cc, dd, ee, X[0], c_uint32(11))\n FF(ee, aa, bb, cc, dd, X[1], c_uint32(14))\n FF(dd, ee, aa, bb, cc, X[2], c_uint32(15))\n FF(cc, dd, ee, aa, bb, X[3], c_uint32(12))\n FF(bb, cc, dd, ee, aa, X[4], c_uint32(5))\n FF(aa, bb, cc, dd, ee, X[5], c_uint32(8))\n FF(ee, aa, bb, cc, dd, X[6], c_uint32(7))\n FF(dd, ee, aa, bb, cc, X[7], c_uint32(9))\n FF(cc, dd, ee, aa, bb, X[8], c_uint32(11))\n FF(bb, cc, dd, ee, aa, X[9], c_uint32(13))\n FF(aa, bb, cc, dd, ee, X[10], c_uint32(14))\n FF(ee, aa, bb, cc, dd, X[11], c_uint32(15))\n FF(dd, ee, aa, bb, cc, X[12], c_uint32(6))\n FF(cc, dd, ee, aa, bb, X[13], c_uint32(7))\n FF(bb, cc, dd, ee, aa, X[14], c_uint32(9))\n FF(aa, bb, cc, dd, ee, X[15], c_uint32(8))\n\n # round 2\n GG(ee, aa, bb, cc, dd, X[7], c_uint32(7))\n GG(dd, ee, aa, bb, cc, X[4], c_uint32(6))\n GG(cc, dd, ee, aa, bb, X[13], c_uint32(8))\n GG(bb, cc, dd, ee, aa, X[1], c_uint32(13))\n GG(aa, bb, cc, dd, ee, X[10], c_uint32(11))\n GG(ee, aa, bb, cc, dd, X[6], c_uint32(9))\n GG(dd, ee, aa, bb, cc, X[15], c_uint32(7))\n GG(cc, dd, ee, aa, bb, X[3], c_uint32(15))\n GG(bb, cc, dd, ee, aa, X[12], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[0], c_uint32(12))\n GG(ee, aa, bb, cc, dd, X[9], c_uint32(15))\n GG(dd, ee, aa, bb, cc, X[5], c_uint32(9))\n GG(cc, dd, ee, aa, bb, X[2], c_uint32(11))\n GG(bb, cc, dd, ee, aa, X[14], c_uint32(7))\n GG(aa, bb, cc, dd, ee, X[11], c_uint32(13))\n GG(ee, aa, bb, cc, dd, X[8], c_uint32(12))\n\n # round 3\n HH(dd, ee, aa, bb, cc, X[3], c_uint32(11))\n HH(cc, dd, ee, aa, bb, X[10], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[14], c_uint32(6))\n HH(aa, bb, cc, dd, ee, X[4], c_uint32(7))\n HH(ee, aa, bb, cc, dd, X[9], c_uint32(14))\n HH(dd, ee, aa, bb, cc, X[15], c_uint32(9))\n HH(cc, dd, ee, aa, bb, X[8], c_uint32(13))\n HH(bb, cc, dd, ee, aa, X[1], c_uint32(15))\n HH(aa, bb, cc, dd, ee, X[2], c_uint32(14))\n HH(ee, aa, bb, cc, dd, X[7], c_uint32(8))\n HH(dd, ee, aa, bb, cc, X[0], c_uint32(13))\n HH(cc, dd, ee, aa, bb, X[6], c_uint32(6))\n HH(bb, cc, dd, ee, aa, X[13], c_uint32(5))\n HH(aa, bb, cc, dd, ee, X[11], c_uint32(12))\n HH(ee, aa, bb, cc, dd, X[5], c_uint32(7))\n HH(dd, ee, aa, bb, cc, X[12], c_uint32(5))\n\n # round 4\n II(cc, dd, ee, aa, bb, X[1], c_uint32(11))\n II(bb, cc, dd, ee, aa, X[9], c_uint32(12))\n II(aa, bb, cc, dd, ee, X[11], c_uint32(14))\n II(ee, aa, bb, cc, dd, X[10], c_uint32(15))\n II(dd, ee, aa, bb, cc, X[0], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[8], c_uint32(15))\n II(bb, cc, dd, ee, aa, X[12], c_uint32(9))\n II(aa, bb, cc, dd, ee, X[4], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[13], c_uint32(9))\n II(dd, ee, aa, bb, cc, X[3], c_uint32(14))\n II(cc, dd, ee, aa, bb, X[7], c_uint32(5))\n II(bb, cc, dd, ee, aa, X[15], c_uint32(6))\n II(aa, bb, cc, dd, ee, X[14], c_uint32(8))\n II(ee, aa, bb, cc, dd, X[5], c_uint32(6))\n II(dd, ee, aa, bb, cc, X[6], c_uint32(5))\n II(cc, dd, ee, aa, bb, X[2], c_uint32(12))\n\n # round 5\n JJ(bb, cc, dd, ee, aa, X[4], c_uint32(9))\n JJ(aa, bb, cc, dd, ee, X[0], c_uint32(15))\n JJ(ee, aa, bb, cc, dd, X[5], c_uint32(5))\n JJ(dd, ee, aa, bb, cc, X[9], c_uint32(11))\n JJ(cc, dd, ee, aa, bb, X[7], c_uint32(6))\n JJ(bb, cc, dd, ee, aa, X[12], c_uint32(8))\n JJ(aa, bb, cc, dd, ee, X[2], c_uint32(13))\n JJ(ee, aa, bb, cc, dd, X[10], c_uint32(12))\n JJ(dd, ee, aa, bb, cc, X[14], c_uint32(5))\n JJ(cc, dd, ee, aa, bb, X[1], c_uint32(12))\n JJ(bb, cc, dd, ee, aa, X[3], c_uint32(13))\n JJ(aa, bb, cc, dd, ee, X[8], c_uint32(14))\n JJ(ee, aa, bb, cc, dd, X[11], c_uint32(11))\n JJ(dd, ee, aa, bb, cc, X[6], c_uint32(8))\n JJ(cc, dd, ee, aa, bb, X[15], c_uint32(5))\n JJ(bb, cc, dd, ee, aa, X[13], c_uint32(6))\n\n # parallel round 1\n JJJ(aaa, bbb, ccc, ddd, eee, X[5], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[14], c_uint32(9))\n JJJ(ddd, eee, aaa, bbb, ccc, X[7], c_uint32(9))\n JJJ(ccc, ddd, eee, aaa, bbb, X[0], c_uint32(11))\n JJJ(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(13))\n JJJ(aaa, bbb, ccc, ddd, eee, X[2], c_uint32(15))\n JJJ(eee, aaa, bbb, ccc, ddd, X[11], c_uint32(15))\n JJJ(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(5))\n JJJ(ccc, ddd, eee, aaa, bbb, X[13], c_uint32(7))\n JJJ(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(7))\n JJJ(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(8))\n JJJ(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(11))\n JJJ(ddd, eee, aaa, bbb, ccc, X[1], c_uint32(14))\n JJJ(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(14))\n JJJ(bbb, ccc, ddd, eee, aaa, X[3], c_uint32(12))\n JJJ(aaa, bbb, ccc, ddd, eee, X[12], c_uint32(6))\n\n # parallel round 2\n III(eee, aaa, bbb, ccc, ddd, X[6], c_uint32(9))\n III(ddd, eee, aaa, bbb, ccc, X[11], c_uint32(13))\n III(ccc, ddd, eee, aaa, bbb, X[3], c_uint32(15))\n III(bbb, ccc, ddd, eee, aaa, X[7], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(12))\n III(eee, aaa, bbb, ccc, ddd, X[13], c_uint32(8))\n III(ddd, eee, aaa, bbb, ccc, X[5], c_uint32(9))\n III(ccc, ddd, eee, aaa, bbb, X[10], c_uint32(11))\n III(bbb, ccc, ddd, eee, aaa, X[14], c_uint32(7))\n III(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(7))\n III(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(12))\n III(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(7))\n III(ccc, ddd, eee, aaa, bbb, X[4], c_uint32(6))\n III(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(15))\n III(aaa, bbb, ccc, ddd, eee, X[1], c_uint32(13))\n III(eee, aaa, bbb, ccc, ddd, X[2], c_uint32(11))\n\n # parallel round 3\n HHH(ddd, eee, aaa, bbb, ccc, X[15], c_uint32(9))\n HHH(ccc, ddd, eee, aaa, bbb, X[5], c_uint32(7))\n HHH(bbb, ccc, ddd, eee, aaa, X[1], c_uint32(15))\n HHH(aaa, bbb, ccc, ddd, eee, X[3], c_uint32(11))\n HHH(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(8))\n HHH(ddd, eee, aaa, bbb, ccc, X[14], c_uint32(6))\n HHH(ccc, ddd, eee, aaa, bbb, X[6], c_uint32(6))\n HHH(bbb, ccc, ddd, eee, aaa, X[9], c_uint32(14))\n HHH(aaa, bbb, ccc, ddd, eee, X[11], c_uint32(12))\n HHH(eee, aaa, bbb, ccc, ddd, X[8], c_uint32(13))\n HHH(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(5))\n HHH(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(14))\n HHH(bbb, ccc, ddd, eee, aaa, X[10], c_uint32(13))\n HHH(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(13))\n HHH(eee, aaa, bbb, ccc, ddd, X[4], c_uint32(7))\n HHH(ddd, eee, aaa, bbb, ccc, X[13], c_uint32(5))\n\n # parallel round 4\n GGG(ccc, ddd, eee, aaa, bbb, X[8], c_uint32(15))\n GGG(bbb, ccc, ddd, eee, aaa, X[6], c_uint32(5))\n GGG(aaa, bbb, ccc, ddd, eee, X[4], c_uint32(8))\n GGG(eee, aaa, bbb, ccc, ddd, X[1], c_uint32(11))\n GGG(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(14))\n GGG(ccc, ddd, eee, aaa, bbb, X[11], c_uint32(14))\n GGG(bbb, ccc, ddd, eee, aaa, X[15], c_uint32(6))\n GGG(aaa, bbb, ccc, ddd, eee, X[0], c_uint32(14))\n GGG(eee, aaa, bbb, ccc, ddd, X[5], c_uint32(6))\n GGG(ddd, eee, aaa, bbb, ccc, X[12], c_uint32(9))\n GGG(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(12))\n GGG(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(9))\n GGG(aaa, bbb, ccc, ddd, eee, X[9], c_uint32(12))\n GGG(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(5))\n GGG(ddd, eee, aaa, bbb, ccc, X[10], c_uint32(15))\n GGG(ccc, ddd, eee, aaa, bbb, X[14], c_uint32(8))\n\n # parallel round 5\n FFF(bbb, ccc, ddd, eee, aaa, X[12], c_uint32(8))\n FFF(aaa, bbb, ccc, ddd, eee, X[15], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[10], c_uint32(12))\n FFF(ddd, eee, aaa, bbb, ccc, X[4], c_uint32(9))\n FFF(ccc, ddd, eee, aaa, bbb, X[1], c_uint32(12))\n FFF(bbb, ccc, ddd, eee, aaa, X[5], c_uint32(5))\n FFF(aaa, bbb, ccc, ddd, eee, X[8], c_uint32(14))\n FFF(eee, aaa, bbb, ccc, ddd, X[7], c_uint32(6))\n FFF(ddd, eee, aaa, bbb, ccc, X[6], c_uint32(8))\n FFF(ccc, ddd, eee, aaa, bbb, X[2], c_uint32(13))\n FFF(bbb, ccc, ddd, eee, aaa, X[13], c_uint32(6))\n FFF(aaa, bbb, ccc, ddd, eee, X[14], c_uint32(5))\n FFF(eee, aaa, bbb, ccc, ddd, X[0], c_uint32(15))\n FFF(ddd, eee, aaa, bbb, ccc, X[3], c_uint32(13))\n FFF(ccc, ddd, eee, aaa, bbb, X[9], c_uint32(11))\n FFF(bbb, ccc, ddd, eee, aaa, X[11], c_uint32(11))\n\n # combine results\n ddd.value += cc.value + MDbuf[1].value # final result for MDbuf[0]\n MDbuf[1].value = MDbuf[2].value + dd.value + eee.value\n MDbuf[2].value = MDbuf[3].value + ee.value + aaa.value\n MDbuf[3].value = MDbuf[4].value + aa.value + bbb.value\n MDbuf[4].value = MDbuf[0].value + bb.value + ccc.value\n MDbuf[0].value = ddd.value\n\n ##### Puts bytes from strptr into X and pad out; appends length\n ##### and finally, compresses the last block(s)\n ##### note: length in bits == 8 * (lswlen + 2^32 mswlen).\n ##### note: there are (lswlen mod 64) bytes left in strptr.\n\n def MDfinish(MDbuf: list[u32], bytes: list[u8], lswlen: u32, mswlen: u32):\n i = c_uint32() # counter\n x = [c_uint32(0) for _ in range(16)]\n str_idx = 0\n\n # /* put bytes from strptr into X\n while i.value \u003C (lswlen.value \u0026 63):\n # byte i goes into word X[i div 4] at pos. 8*(i mod 4)\n x[i.value \u003E\u003E 2].value ^= (\n c_uint32(bytes[str_idx].value).value\n \u003C\u003C c_uint32((8 * (i.value \u0026 3))).value\n )\n i.value += 1\n str_idx += 1\n\n # append the bit m_n == 1\n x[(lswlen.value \u003E\u003E c_uint32(2).value) \u0026 15].value ^= c_uint32(1).value \u003C\u003C (\n 8 * (lswlen.value \u0026 3) + 7\n )\n\n if (lswlen.value \u0026 c_uint32(63).value) \u003E 55:\n compress(MDbuf, x)\n x = [c_uint32(0) for _ in range(16)]\n\n # append length in bits\n x[14].value = lswlen.value \u003C\u003C c_uint32(3).value\n x[15].value = (lswlen.value \u003E\u003E c_uint32(29).value) | (\n mswlen.value \u003C\u003C c_uint32(3).value\n )\n compress(MDbuf, x)\n\n RMDsize = 160\n\n # Converts 4 u8s into 1 u32\n def u8_to_u32(strptr: list[u8]):\n return c_uint32(\n ((strptr[3]).value \u003C\u003C 24)\n | ((strptr[2]).value \u003C\u003C 16)\n | ((strptr[1]).value \u003C\u003C 8)\n | ((strptr[0].value))\n )\n\n ##### Returns the RIPEMD-160 digest of the message in bytes #####\n def RMD(message: list[u8]):\n MDbuf = [\n c_uint32(0) for _ in range((RMDsize / 32).__floor__())\n ] # contains (A, B, C, D, E)\n hashcode = [c_uint8(0) for _ in range((RMDsize / 8).__floor__())]\n # for final hash-value\n x = [c_uint32(0) for _ in range(16)] # current 16-word chunk\n i = c_uint32(0) # counter\n length = message.__len__() # length in bytes of message\n nbytes = message.__len__() # number of bytes not yet processed\n message_idx = 0\n\n MDinit(MDbuf)\n\n # process message in 16-word chunks\n\n while nbytes \u003E 63:\n i.value = 0\n while i.value \u003C 16:\n x[i.value] = u8_to_u32(message[message_idx:])\n message_idx += 4\n i.value += 1\n compress(MDbuf, x)\n nbytes -= 64\n\n # finish:\n MDfinish(MDbuf, message, c_uint32(length), c_uint32(0))\n i.value = 0\n while i.value \u003C RMDsize / 8:\n hashcode[i.value] = c_uint8(MDbuf[i.value \u003E\u003E 2].value)\n hashcode[i.value + 1] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 8)\n hashcode[i.value + 2] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 16)\n hashcode[i.value + 3] = c_uint8(MDbuf[i.value \u003E\u003E 2].value \u003E\u003E 24)\n i.value += 4\n\n return hashcode\n\n ##### Returns the RIPEMD-160 hash of the message in a string #####\n def RMDstring(message: str):\n i = 0\n encoded_message: list[c_uint8] = [c_uint8(x) for x in list(message.encode())]\n\n hashcode = RMD(encoded_message)\n\n string = \"\"\n for i in hashcode:\n string += f\"{i:02x}\"\n return string\n\n return RMDstring(Z10139K1)\n"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "Pure Python"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
cp8shzkg1nlaciqjvxgsi978ndxc2yu
Template:About
10
85254
281222
2005-06-28T04:28:39Z
en>Cantus
0
281222
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}; for other meanings, see'' [[{{PAGENAME}} (disambiguation)]].</span>
3vnl1ljez6yh5kw9vl3rdfyx0cyybwp
281223
281222
2005-06-28T08:06:00Z
en>Radiant!
0
empty page & protect, to keep people from recreating (take to VFU if needed)
281223
wikitext
text/x-wiki
-
6za6z27cqk2qatlebqsiz85czbjf3rd
281224
281223
2005-08-03T17:06:20Z
en>Nohat
0
actual new otheruses template
281224
wikitext
text/x-wiki
This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].
nt5yheae9m6x8qmjnj10o2t5ak9o6gy
281225
281224
2005-08-03T17:07:19Z
en>Nohat
0
281225
wikitext
text/x-wiki
:''This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].''
kob85h8qnsppifgvakkb6e740n9tbqp
281226
281225
2005-08-03T17:14:31Z
en>Nohat
0
add span used in other dab templates
281226
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].''</span>
rr6ung9ogr42is2ktplbgvrirpsagt0
281227
281226
2005-08-04T10:24:03Z
en>Cantus
0
281227
wikitext
text/x-wiki
<div class="dablink"><span style="margin-left: 2em;">''This article is about {{{1}}}; for {{{2}}}, see'' [[{{{3}}}]].</span></div>
59c9bilmi2gnhggm7npu83uqbvl81vg
281228
2005-03-12T08:40:33Z
en>Docu
0
similar to [[Template:Otheruses3|Otheruses3]]
281228
wikitext
text/x-wiki
: <span class="dablink">''{{{1}}}.''</span>
i908jmrkajsc78rp39s6clomzakc4uq
281229
281228
2005-06-13T09:29:10Z
en>LostAccount
0
281229
wikitext
text/x-wiki
''For other uses, see {{{1}}}.''
7seyouhc3i95ma1aqb3h7fyw653nf4d
281230
281229
2005-06-13T09:31:10Z
en>LostAccount
0
281230
wikitext
text/x-wiki
''For other uses, see [[{{{1}}}]].''
srt17md598jynj8yzi80rjpcgc3lzxe
281231
281230
2005-06-13T10:49:56Z
en>LostAccount
0
281231
wikitext
text/x-wiki
{{d}}
''For other uses, see [[{{{1}}}]].''
9a2790b0l756xtgn1ks86whmngdefxh
281232
281231
2005-06-13T10:51:07Z
en>LostAccount
0
281232
wikitext
text/x-wiki
{{tfd}}
k9l8xqlcskczrztpt38a6i48ombwx6z
281233
281232
2005-06-17T01:37:17Z
en>Cantus
0
281233
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}; for other meanings, see'' [[{{PAGENAME}} (disambiguation)]].</span>
3vnl1ljez6yh5kw9vl3rdfyx0cyybwp
281234
281227
2005-12-05T06:46:35Z
en>Fitch
0
match the margin and other settings of all the other otheruses templates
281234
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}; for {{{2}}}, see'' [[{{{3}}}]].</span>
dah8621s8ps9uxn9fw8ixpc3y7eyrwp
281235
281234
2005-12-09T10:35:12Z
en>Jiy
0
making consistent with other templates of this nature
281235
wikitext
text/x-wiki
:''<span class="dablink">This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].</span>''
oin93sg70flkute4ox78gjb69jujz73
281236
281235
2006-01-08T09:53:57Z
en>Netoholic
0
class="notice
281236
wikitext
text/x-wiki
<div class="notice dablink">''This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].''</div>
5yd0rqijsw11786zqovbt5qtl8541rg
281237
281236
2006-01-10T15:27:28Z
en>Ed g2s
0
Reverted edits by [[Special:Contributions/Netoholic|Netoholic]] ([[User talk:Netoholic|talk]]) to last version by Jiy
281237
wikitext
text/x-wiki
:''<span class="dablink">This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].</span>''
oin93sg70flkute4ox78gjb69jujz73
281238
281237
2006-01-26T22:50:00Z
en>GoldRingChip
0
281238
wikitext
text/x-wiki
:''<span class="dablink">This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].</span>''<noinclude>[[Category:Disambiguation and Redirection templates|Otheruses4]]</noinclude>
ma12gaglauk13fnfd8r31yggikaq2pj
281239
281238
2006-02-16T04:18:52Z
en>ApprenticeFan
0
281239
wikitext
text/x-wiki
:''<span class="dablink">This article is about {{{1}}}. For other uses, see [[{{{2}}}]].</span>''<noinclude>[[Category:Disambiguation and Redirection templates|Otheruses4]]</noinclude>
belvn97jepn5hzmpjkub4zzzm0kshl2
281240
281239
2006-02-16T04:22:53Z
en>Steinsky
0
Reverted edits by [[Special:Contributions/ApprenticeFan|ApprenticeFan]] ([[User talk:ApprenticeFan|talk]]) to last version by Markles
281240
wikitext
text/x-wiki
:''<span class="dablink">This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].</span>''<noinclude>[[Category:Disambiguation and Redirection templates|Otheruses4]]</noinclude>
ma12gaglauk13fnfd8r31yggikaq2pj
281241
281240
2006-02-20T20:46:17Z
en>Gracefool
0
/* See also */
281241
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].''</span><noinclude>
==See also==
* {{tl|otheruses}}
* {{tl|redirect}}
[[Category:Disambiguation and Redirection templates|Otheruses4]]</noinclude>
0wopsiclb23n9akq9493unbdf1e4267
281242
281241
2006-03-05T06:01:24Z
en>Catapult
0
fixed capitalization of category[[user:freakofnurture|...]]
281242
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].''</span><noinclude>
==See also==
* {{tl|otheruses}}
* {{tl|redirect}}
[[Category:Disambiguation and redirection templates|Otheruses4]]</noinclude>
3soj0wbanf1pmzckg7qyg2pywzxx20d
281243
281242
2006-03-06T00:30:37Z
en>Simetrical
0
Add Template:Otheruses templates to assist people in finding the right template
281243
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].''</span><noinclude>
{{Otheruses templates}}</noinclude>
p009eklddeznmp0ir9yw6nuz8gcouow
281244
281243
2006-04-13T16:40:09Z
en>Ed g2s
0
remove redundant information - these templates have been deleted before
281244
wikitext
text/x-wiki
:<span class="dablink">''For {{{2}}}, see [[{{{3}}}]].''</span><noinclude>
{{Otheruses templates}}</noinclude>
4j0to3gblgsfn5lrep1k1kwbe74d4nx
281245
281244
2006-04-16T01:46:45Z
en>Chris the speller
0
rv Ed g2s's abrupt redirect, to last version by Simetrical
281245
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].''</span><noinclude>
{{Otheruses templates}}</noinclude>
p009eklddeznmp0ir9yw6nuz8gcouow
281246
281245
2006-04-17T06:41:32Z
en>William Allen Simpson
0
For further information, see talk page.
281246
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
1v4x333b07qoo0iyo5qbz75pgc18r64
281247
281246
2006-04-23T04:47:13Z
en>Simetrical
0
Add some sensible defaults
281247
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
ilfwy5xliu4og5ohq61g8o4c65g52lp
281248
281247
2006-04-23T05:35:15Z
en>Simetrical
0
Add a grossly late {{tfd}}
281248
wikitext
text/x-wiki
{{tfd|Otheruses4}}
:<span class="dablink">''This article is about {{{1}}}. For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
mbk0jn57wcilvodyrnn7cbvbcxmz74h
281249
281248
2006-04-26T05:28:12Z
en>Zzyzx11
0
rm {{tfd}}, see [[Wikipedia:Templates for deletion/Log/2006 April 18]]
281249
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
ilfwy5xliu4og5ohq61g8o4c65g52lp
281250
281249
2006-04-30T23:37:38Z
en>Arniep
0
rv to William Allen Simpson
281250
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2}}}, see [[{{{3}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
1v4x333b07qoo0iyo5qbz75pgc18r64
281251
281250
2006-05-01T03:38:30Z
en>Simetrical
0
Revert. Please explain why you want to remove the parameter defaults?
281251
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
ilfwy5xliu4og5ohq61g8o4c65g52lp
281252
281251
2006-05-01T03:40:53Z
en>Simetrical
0
Don't display intro sentence if first parameter doesn't exist
281252
wikitext
text/x-wiki
:<span class="dablink">''{{#ifexpr: {{{1|}}}|This article is about {{{1}}}. }}For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
0c1045hzkm0yv5ur3niooitv4xvflke
281253
281252
2006-05-01T04:05:00Z
en>Esperant
0
Revert to revision dated 13:38, 1 May 2006 by Simetrical, oldid 50993426 using [[:en:Wikipedia:Tools/Navigation_popups|popups]]
281253
wikitext
text/x-wiki
:<span class="dablink">''This article is about {{{1}}}. For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
ilfwy5xliu4og5ohq61g8o4c65g52lp
281254
281253
2006-05-01T21:02:21Z
en>Simetrical
0
Cautiously revert with modifications that will hopefully solve the problem Mgekelly reverted for (checking if it works)
281254
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
ts3ebwc02oldbkx8ctd8z4su978pxsp
281255
281254
2006-05-12T22:58:20Z
en>Aaru Bui
0
281255
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{{2|other uses of the term}}}, see [[{{{4|3}}}|{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
ruxk5dmde9mkqh5l4uo0oteavnt9r1y
281256
281255
2006-05-12T23:02:54Z
en>Aaru Bui
0
281256
wikitext
text/x-wiki
#REDIRECT [[Template:Otheruses templates]]
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
gj8ec1b00daacb5y914xa5m0zoic9gd
281257
281256
2006-05-12T23:03:27Z
en>Aaru Bui
0
281257
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
ts3ebwc02oldbkx8ctd8z4su978pxsp
281258
281257
2006-05-12T23:04:04Z
en>Aaru Bui
0
281258
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
#REDIRECT [[Template:Otheruses templates]]
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
3y7990m5oe3zs220t6kxzy1oudjn1z0
281259
281258
2006-05-12T23:04:26Z
en>Aaru Bui
0
281259
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
ts3ebwc02oldbkx8ctd8z4su978pxsp
281260
281259
2006-05-12T23:09:13Z
en>Aaru Bui
0
281260
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{{2|other uses of the term}}}, see [[{{{4|3}}}|{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
ruxk5dmde9mkqh5l4uo0oteavnt9r1y
281261
281260
2006-05-12T23:24:12Z
82.3.111.249
rv
281261
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
ts3ebwc02oldbkx8ctd8z4su978pxsp
281262
281261
2006-05-16T04:54:18Z
en>Simetrical
0
Try to change formatting so that {{{2}}} can be omitted by adding an extra pipe
281262
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses of the term}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
<!--Other languages-->
</noinclude>
76lyf4zetabhnijuia5jlaoumrpddou
281263
281262
2006-05-16T08:44:06Z
en>Percy Snoodle
0
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]]
281263
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses of the term}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]]
<!--Other languages-->
</noinclude>
6y9fa5ykxr1lgh0ezmeg5015yzry1zw
281264
281263
2006-05-18T04:20:46Z
en>Simetrical
0
More flexibility
281264
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses of the term}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]]
<!--Other languages-->
</noinclude>
ehwnltxr1r5juaeru7q1ep1cv2sgoaq
281265
281264
2006-06-30T17:41:46Z
en>Simetrical
0
This workaround makes no sense. Testing whether it's necessary . . .
281265
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{{2|other uses of the term}}}, see [[{{{3|{{PAGENAME}} (disambiguation)}}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]]
<!--Other languages-->
</noinclude>
6wp3rm870oqvlygw7wyeuped4sstl3p
281266
281265
2006-06-30T17:42:19Z
en>Simetrical
0
Revert to revision 53806306 dated 2006-05-18 04:20:46 by Simetrical using [[:en:Wikipedia:Tools/Navigation_popups|popups]]
281266
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses of the term}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]]
<!--Other languages-->
</noinclude>
ehwnltxr1r5juaeru7q1ep1cv2sgoaq
281267
281266
2006-07-03T18:10:14Z
en>Magister Mathematicae
0
Protected Template:Otheruses4: high visibility template [edit=sysop:move=sysop]
281266
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses of the term}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]]
<!--Other languages-->
</noinclude>
ehwnltxr1r5juaeru7q1ep1cv2sgoaq
281268
281267
2006-07-03T18:11:06Z
en>Magister Mathematicae
0
281268
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses of the term}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
9fphlag5wqg7avr3dybhz1pgpn7aj5f
281269
281268
2006-07-04T10:28:51Z
en>Freakofnurture
0
rm. "of the term"
281269
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
in13ati6z6cpgr1lmt994s7p1ws1mnr
281270
281269
2006-11-02T19:02:21Z
en>Omegatron
0
«"uses" → "articles with similar names"» updating to get rid of ambiguity of the word "uses", as per [[Template_talk:Otheruses#All_the_meaning_problems...]]
281270
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other articles with similar names}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
56edt270g0q50udjk9vrvkeljgfpln2
281271
281270
2006-11-08T11:26:10Z
en>Robdurbar
0
Protected Template:Otheruses4: reduce to semi protection [edit=autoconfirmed:move=autoconfirmed]
281270
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other articles with similar names}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
56edt270g0q50udjk9vrvkeljgfpln2
281272
281271
2006-11-08T17:57:03Z
en>Night Gyr
0
Revert to revision 62000221 dated 2006-07-04 10:28:51 by Freakofnurture using [[:en:Wikipedia:Tools/Navigation_popups|popups]]
281272
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
in13ati6z6cpgr1lmt994s7p1ws1mnr
281273
281272
2006-12-09T01:24:27Z
en>Centrx
0
rv
281273
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other articles with similar names}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
56edt270g0q50udjk9vrvkeljgfpln2
281274
281273
2006-12-09T20:06:19Z
en>Night Gyr
0
we've been over this before, don't make these changes without consensus
281274
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
in13ati6z6cpgr1lmt994s7p1ws1mnr
281275
281274
2006-12-28T18:34:20Z
en>Renesis
0
Protected Template:Otheruses4: High-risk template; Change to full protection [edit=sysop:move=sysop]
281274
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
in13ati6z6cpgr1lmt994s7p1ws1mnr
281276
281275
2006-12-28T18:34:50Z
en>Renesis
0
+{{hprotected}}
281276
wikitext
text/x-wiki
:<span class="dablink">''{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].''</span><noinclude>
{{hprotected}}
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
oodwvcfjlt2x2bf3hhv3nzn57yd4pdt
281277
281276
2006-12-29T21:04:24Z
en>Omegatron
0
Implemented in site-wide CSS. [[Wikipedia:Bypass your cache|bypass your cache]] to see it.
281277
wikitext
text/x-wiki
<span class="dablink">{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].</span><noinclude>
{{hprotected}}
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
qorx6dwsybeno977v7vt9b8o5b4fx7j
281278
281277
2006-12-29T22:38:50Z
en>Omegatron
0
fix linebreak/whitespace
281278
wikitext
text/x-wiki
<p class="dablink">{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].</p><noinclude>
{{hprotected}}
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
i6qpk1oaqry9bg2r6b8bicoy1cwfe1w
281279
281278
2006-12-30T07:42:13Z
en>Omegatron
0
Inherit formatting and style from dablink template
281279
wikitext
text/x-wiki
{{dablink|{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].}}<noinclude>
{{hprotected}}
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
</noinclude>
22nkwgd2lokbvie515m8egv2cvljz30
281280
281279
2007-01-14T00:28:27Z
en>Luna Santin
0
protected edit requested, see talk
281280
wikitext
text/x-wiki
{{dablink|{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].}}<noinclude>
{{hprotected}}
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
[[vi:Tiêu bản:Otheruses4]]
</noinclude>
rq10071foxxws7tgg0c4omife5jmm20
281281
281280
2007-03-22T13:28:48Z
en>Harryboyles
0
add interwiki link per request on talk page
281281
wikitext
text/x-wiki
{{dablink|{{#if: {{{1|}}}|This article is about {{{1}}}. |}}For {{#if: {{{2|}}}|{{{2}}}|other uses}}, see [[{{#if: {{{3|}}}|{{{3}}}|{{PAGENAME}} (disambiguation)}}]].}}<noinclude>
{{hprotected}}
----
For further information, see talk page.
[[Category:Disambiguation and redirection templates|{{PAGENAME}}]] The <nowiki>{{about}}</nowiki> template redirects here, so take that into account while considering visibility.
<!--Other languages-->
[[sl:Predloga:Drugipomeni4]]
[[vi:Tiêu bản:Otheruses4]]
</noinclude>
gxhzwj3m26wwr3fr3318mbsk8gric6b
281282
281281
2007-08-09T16:48:05Z
en>MZMcBride
0
made change per talk request
281282
wikitext
text/x-wiki
{{dablink|{{#if: {{{1|}}}|This article is about {{{1}}}.  }}{{#if:{{{2|}}}|For {{{2}}}, see {{#if:{{{3|}}}|[[{{{3}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  |For other uses, see {{#if:{{{3|}}}|[[{{{3}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}{{#if:{{{2|}}}|{{#if:{{{4|}}}|{{#ifeq:{{{4}}}|1|For other uses, see {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{4}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  }}}}{{#if:{{{4|}}}|{{#if:{{{6|}}}|{{#ifeq:{{{6}}}|1|For other uses, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{6}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  }}}}{{#if:{{{6|}}}|{{#if:{{{8|}}}|{{#ifeq:{{{8}}}|1|For other uses, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{8}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  }}}}}}}}}}}}<noinclude>{{pp-template|small=yes}}{{template doc}}</noinclude>
eiffbre0muq1n8atx53dr5syikeszsi
281283
281282
2008-06-01T15:48:10Z
en>Happy-melon
0
display "this page..." outside ns:0, per editprotected
281283
wikitext
text/x-wiki
{{dablink|{{#if: {{{1|}}}|This {{#ifeq:{{NAMESPACE}}|{{ns:0}}|article|page}} is about {{{1}}}.  }}{{#if:{{{2|}}}|For {{{2}}}, see {{#if:{{{3|}}}|[[{{{3}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  |For other uses, see {{#if:{{{3|}}}|[[{{{3}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}{{#if:{{{2|}}}|{{#if:{{{4|}}}|{{#ifeq:{{{4}}}|1|For other uses, see {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{4}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  }}}}{{#if:{{{4|}}}|{{#if:{{{6|}}}|{{#ifeq:{{{6}}}|1|For other uses, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{6}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  }}}}{{#if:{{{6|}}}|{{#if:{{{8|}}}|{{#ifeq:{{{8}}}|1|For other uses, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{8}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  }}}}}}}}}}}}<noinclude>{{pp-template|small=yes}}{{template doc}}</noinclude>
8z10443bvq43jtx3r9zbgnxgur14d6u
281284
281283
2009-09-09T04:23:09Z
en>Plastikspork
0
moved [[Template:Otheruses4]] to [[Template:About]]: More intuitive name per discussion
281283
wikitext
text/x-wiki
{{dablink|{{#if: {{{1|}}}|This {{#ifeq:{{NAMESPACE}}|{{ns:0}}|article|page}} is about {{{1}}}.  }}{{#if:{{{2|}}}|For {{{2}}}, see {{#if:{{{3|}}}|[[{{{3}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  |For other uses, see {{#if:{{{3|}}}|[[{{{3}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}{{#if:{{{2|}}}|{{#if:{{{4|}}}|{{#ifeq:{{{4}}}|1|For other uses, see {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{4}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  }}}}{{#if:{{{4|}}}|{{#if:{{{6|}}}|{{#ifeq:{{{6}}}|1|For other uses, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{6}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  }}}}{{#if:{{{6|}}}|{{#if:{{{8|}}}|{{#ifeq:{{{8}}}|1|For other uses, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{8}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.  }}}}}}}}}}}}<noinclude>{{pp-template|small=yes}}{{template doc}}</noinclude>
8z10443bvq43jtx3r9zbgnxgur14d6u
281285
281284
2009-11-03T10:13:26Z
en>MSGJ
0
improve code readability
281285
wikitext
text/x-wiki
{{dablink|{{#if:{{{1|}}}
|This {{#ifeq:{{NAMESPACE}}|{{ns:0}}|article|page}} is about {{{1}}}.  
}}{{#if:{{{2|}}}
|For {{{2}}}, see {{#if:{{{3|}}}
|[[{{{3}}}]]
|[[{{PAGENAME}} (disambiguation)]]
}}.  
|For other uses, see {{#if:{{{3|}}}
|[[{{{3}}}]]
|[[{{PAGENAME}} (disambiguation)]]
}}.
}}{{#if:{{{2|}}}
|{{#if:{{{4|}}}
|{{#ifeq:{{{4}}}|1
|For other uses, see {{#if:{{{5|}}}
|[[{{{5}}}]]
|[[{{PAGENAME}} (disambiguation)]]
}}.
|For {{{4}}}, see {{#if:{{{5|}}}
|[[{{{5}}}]]
|[[{{PAGENAME}} (disambiguation)]]
}}.  
}}
}}{{#if:{{{4|}}}
|{{#if:{{{6|}}}
|{{#ifeq:{{{6}}}|1
|For other uses, see {{#if:{{{7|}}}
|[[{{{7}}}]]
|[[{{PAGENAME}} (disambiguation)]]
}}.
|For {{{6}}}, see {{#if:{{{7|}}}
|[[{{{7}}}]]
|[[{{PAGENAME}} (disambiguation)]]
}}.  
}}
}}{{#if:{{{6|}}}
|{{#if:{{{8|}}}
|{{#ifeq:{{{8}}}|1
|For other uses, see {{#if:{{{9|}}}
|[[{{{9}}}]]
|[[{{PAGENAME}} (disambiguation)]]
}}.
|For {{{8}}}, see {{#if:{{{9|}}}
|[[{{{9}}}]]
|[[{{PAGENAME}} (disambiguation)]]
}}.  
}}
}}
}}
}}
}}
}}<noinclude>
{{pp-template|small=yes}}
{{documentation}}
</noinclude>
1bs63cxxwf596etdyabme2rxy1ep8t8
281286
281285
2009-11-03T12:44:34Z
en>TheDJ
0
refactor per talkpage edit request. Adds support for "and" as used by {{otheruses6}} and {{otheruses8}}
281286
wikitext
text/x-wiki
{{dablink|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{#ifeq:{{NAMESPACE}}|{{ns:0}}|article|page}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<noinclude>
{{Documentation}}
</noinclude>
srfsc3opn5yzw65y36rkf7x6q5xcc5c
281287
281286
2010-09-05T19:32:21Z
en>R'n'B
0
replace links with [[Template:(D)]] for intentional links to disambiguation pages; tested in Sandbox
281287
wikitext
text/x-wiki
{{dablink|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{#ifeq:{{NAMESPACE}}|{{ns:0}}|article|page}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|{{(D)|{{{3}}}}}{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|{{(D)|{{{5}}}}}|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|{{(D)|{{{5}}}}}{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|{{(D)|{{{7}}}}}|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|{{(D)|{{{7}}}}}{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|{{(D)|{{{9}}}}}|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|{{(D)|{{{9}}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<noinclude>
{{Documentation}}
</noinclude>
pwn938dkg0lkl9xra6j3lodxprtu7hk
281288
281287
2010-09-05T23:50:15Z
en>Magioladitis
0
Undid revision 383107325 by [[Special:Contributions/R'n'B|R'n'B]] ([[User talk:R'n'B|talk]]) this broke something
281288
wikitext
text/x-wiki
{{dablink|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{#ifeq:{{NAMESPACE}}|{{ns:0}}|article|page}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<noinclude>
{{Documentation}}
</noinclude>
srfsc3opn5yzw65y36rkf7x6q5xcc5c
281289
281288
2011-02-26T21:26:46Z
en>Plastikspork
0
Tracking for first two empty, but third is not, probably replacable by {{other uses|...}} and {{about|is a ...}}, which is just wrong :)
281289
wikitext
text/x-wiki
{{dablink|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{#ifeq:{{NAMESPACE}}|{{ns:0}}|article|page}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<!--
-->{{#if:{{{1|}}}{{{2|}}}||{{#if:{{{3|}}}|[[Category:Hatnote templates using unusual parameters|A{{PAGENAME}}]]}}}}{{#ifeq:{{str left|{{{1}}}|3}}|is |[[Category:Hatnote templates using unusual parameters|B{{PAGENAME}}]]}}<noinclude>
{{Documentation}}
</noinclude>
40a4m67788x1oyhu2g4tsupioa29yyn
281290
281289
2011-02-26T22:15:11Z
en>Plastikspork
0
add option to not track in tracking category
281290
wikitext
text/x-wiki
{{dablink|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{#ifeq:{{NAMESPACE}}|{{ns:0}}|article|page}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<!--
-->{{#if:{{{_nocat|}}}||{{#if:{{{1|}}}{{{2|}}}||{{#if:{{{3|}}}|[[Category:Hatnote templates using unusual parameters|A{{PAGENAME}}]]}}}}{{#ifeq:{{str left|{{{1}}}|3}}|is |[[Category:Hatnote templates using unusual parameters|B{{PAGENAME}}]]}}}}<noinclude>
{{Documentation}}
</noinclude>
m30vbkc8oowcb5xisgazm79twgzd07n
281291
281290
2011-10-24T00:01:44Z
en>Rich Farmbrough
0
281291
wikitext
text/x-wiki
{{Hatnote|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{#ifeq:{{NAMESPACE}}|{{ns:0}}|article|page}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<!--
-->{{#if:{{{_nocat|}}}||{{#if:{{{1|}}}{{{2|}}}||{{#if:{{{3|}}}|[[Category:Hatnote templates using unusual parameters|A{{PAGENAME}}]]}}}}{{#ifeq:{{str left|{{{1}}}|3}}|is |[[Category:Hatnote templates using unusual parameters|B{{PAGENAME}}]]}}}}<noinclude>
{{Documentation}}
</noinclude>
4n5pjjx3aoc6sxwl4xwbxx4zihiltri
281292
281291
2012-02-09T14:23:33Z
en>Anomie
0
Display "This category is about" for Category-namespace pages, per talk request
281292
wikitext
text/x-wiki
{{Hatnote|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{namespace detect|main=article|category=category|other=page}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<!--
-->{{#if:{{{_nocat|}}}||{{#if:{{{1|}}}{{{2|}}}||{{#if:{{{3|}}}|[[Category:Hatnote templates using unusual parameters|A{{PAGENAME}}]]}}}}{{#ifeq:{{str left|{{{1}}}|3}}|is |[[Category:Hatnote templates using unusual parameters|B{{PAGENAME}}]]}}}}<noinclude>
{{Documentation}}
</noinclude>
12dmka8m2zke9h8ww7o249ge1jh7t38
281293
281292
2013-11-02T02:30:19Z
en>Fuhghettaboutit
0
Changed protection level of Template:About: Enable access by template editors ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite))
281292
wikitext
text/x-wiki
{{Hatnote|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{namespace detect|main=article|category=category|other=page}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<!--
-->{{#if:{{{_nocat|}}}||{{#if:{{{1|}}}{{{2|}}}||{{#if:{{{3|}}}|[[Category:Hatnote templates using unusual parameters|A{{PAGENAME}}]]}}}}{{#ifeq:{{str left|{{{1}}}|3}}|is |[[Category:Hatnote templates using unusual parameters|B{{PAGENAME}}]]}}}}<noinclude>
{{Documentation}}
</noinclude>
12dmka8m2zke9h8ww7o249ge1jh7t38
281294
281293
2014-05-05T05:50:18Z
en>Steel1943
0
TESTED edit on the [[/sandbox]] that allows template to render a "This section..." wording instead of a "This page/category/article..." wording without breaking any currently-existing transcluded templates - please see the [[/testcases]] subpage
281294
wikitext
text/x-wiki
{{Hatnote|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{#if:{{{section|}}}|section|{{namespace detect|main=article|category=category|other=page}}}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<!--
-->{{#if:{{{_nocat|}}}||{{#if:{{{1|}}}{{{2|}}}||{{#if:{{{3|}}}|[[Category:Hatnote templates using unusual parameters|A{{PAGENAME}}]]}}}}{{#ifeq:{{str left|{{{1}}}|3}}|is |[[Category:Hatnote templates using unusual parameters|B{{PAGENAME}}]]}}}}<noinclude>
{{Documentation}}
</noinclude>
4dkwz82scbdqvofoyxhib6eorkwd57z
281295
281294
2015-07-26T03:18:26Z
en>Jackmcbarn
0
avoid calling [[Module:Namespace detect]] unnecessarily
281295
wikitext
text/x-wiki
{{Hatnote|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{#if:{{{section|}}}|section|{{#switch:{{NAMESPACENUMBER}}|0=article|14=category|page}}}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<!--
-->{{#if:{{{_nocat|}}}||{{#if:{{{1|}}}{{{2|}}}||{{#if:{{{3|}}}|[[Category:Hatnote templates using unusual parameters|A{{PAGENAME}}]]}}}}{{#ifeq:{{str left|{{{1}}}|3}}|is |[[Category:Hatnote templates using unusual parameters|B{{PAGENAME}}]]}}}}<noinclude>
{{Documentation}}
</noinclude>
fhh3qw0xqt4dvrq705j8pkd7451fs7o
281296
281295
2016-05-01T04:25:37Z
en>Nihiltres
0
Added tests for a set of unusual parameterizations
281296
wikitext
text/x-wiki
{{Hatnote|<!--
-->{{#if: {{{1|}}}|<!--
-->This {{#if:{{{section|}}}|section|{{#switch:{{NAMESPACENUMBER}}|0=article|14=category|page}}}} is about {{{1}}}.  }}<!--
-->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and| and {{#if:{{{5|}}}|[[{{{5}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.<!--
-->{{#if:{{{2|}}}|{{#if:{{{4|}}}|<!--
-->{{#ifeq:{{{4|}}}|and||<!-- "and" is a special word, don't output "For and, ..."
-->  For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and| and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{6|}}}|<!--
-->{{#ifeq:{{{6|}}}|and||<!--
-->  For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and| and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->{{#if:{{{8|}}}|<!--
-->{{#ifeq:{{{8|}}}|and||<!--
-->  For {{#ifeq:{{{8}}}|1|other uses|{{{8}}}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.}}<!--
-->}}<!--
-->}}<!--
-->}}}}}}<!--
-->{{#if:{{{_nocat|}}}||{{#if:{{{1|}}}{{{2|}}}||{{#if:{{{3|}}}|[[Category:Hatnote templates using unusual parameters|A{{PAGENAME}}]]}}}}{{#ifeq:{{str left|{{{1}}}|3}}|is |[[Category:Hatnote templates using unusual parameters|B{{PAGENAME}}]]}}}}<!--
-->{{#ifeq:{{#if:{{{5|}}}{{{6|}}}{{{7|}}}{{{8|}}}{{{9|}}}|a}}{{#if:{{{4|}}}||b}}|ab|[[Category:Hatnote templates using unusual parameters]]}}<!--If 5 to 9 and not 4
-->{{#ifeq:{{#if:{{{7|}}}{{{8|}}}{{{9|}}}|a}}{{#if:{{{6|}}}||b}}|ab|[[Category:Hatnote templates using unusual parameters]]}}<!--If 7 to 9 and not 6
-->{{#ifeq:{{#if:{{{9|}}}|a}}{{#if:{{{8|}}}||b}}|ab|[[Category:Hatnote templates using unusual parameters]]}}<!--If 9 and not 8
--><noinclude>
{{Documentation}}
</noinclude>
1l1fwbjxh23oidzzjsydof9joayt34n
281297
281296
2016-05-07T00:02:43Z
en>Nihiltres
0
Major update: reimplemented in Lua (via [[Module:About]]) as tested in sandbox.
281297
wikitext
text/x-wiki
<includeonly>{{#invoke:about|about}}</includeonly><noinclude>{{Documentation}}</noinclude>
sewwzp1otrriymoi2xy4csy1ag24qbn
281298
281297
2018-02-18T23:22:11Z
en>JJMC89
0
[[Wikipedia:Templates for discussion/Log/2018 February 18#Template:About|Nominated for merging]]
281298
wikitext
text/x-wiki
{{Tfm/dated|page=About|otherpage=About2|link=Wikipedia:Templates for discussion/Log/2018 February 18#Template:About|type=tiny|bigbox={{#invoke:Noinclude|noinclude|text=yes}}}}<includeonly>{{#invoke:about|about}}</includeonly><noinclude>{{Documentation}}</noinclude>
m7ekgqrv8yz20wky601x48lmzbjvmex
281299
281298
2018-02-19T02:59:33Z
en>The Earwig
0
<noinclude> TfD notification; looks awful on 100,000+ pages (see talk)
281299
wikitext
text/x-wiki
<noinclude>{{Tfm/dated|page=About|otherpage=About2|link=Wikipedia:Templates for discussion/Log/2018 February 18#Template:About|type=tiny|bigbox={{#invoke:Noinclude|noinclude|text=yes}}}}</noinclude><includeonly>{{#invoke:about|about}}</includeonly><noinclude>{{Documentation}}</noinclude>
lledgeu4doxxtwazjuv80xawq9axoga
281300
281299
2018-02-27T17:34:08Z
en>Primefac
0
[[Wikipedia:Templates for discussion/Log/2018 February 18#Template:Redirect3_and_Template:About2]] closed as merge ([[WP:XFDC|XFDcloser]])
281300
wikitext
text/x-wiki
<includeonly>{{#invoke:about|about}}</includeonly><noinclude>{{Documentation}}</noinclude>
sewwzp1otrriymoi2xy4csy1ag24qbn
281301
281300
2021-09-15T23:19:44Z
en>Wbm1058
0
populate [[Category:Articles with Template:About targeting a nonexistent page]]
281301
wikitext
text/x-wiki
<includeonly>{{#invoke:about|about}}<includeonly>{{Main other|{{#if:{{{4|}}}|{{#if:{{{5|}}}|{{#ifexist:{{{5}}}| |[[Category:Articles with Template:About targeting a nonexistent page]]}}}}}}}}
</includeonly><noinclude>
{{Documentation}}
</noinclude>
45fr1bc44ernrxbcr8rqjszo4s89xfx
281302
281301
2021-09-16T00:37:55Z
en>Wbm1058
0
281302
wikitext
text/x-wiki
{{#invoke:about|about}}<includeonly>{{Main other|{{#if:{{{4|}}}|{{#if:{{{5|}}}|{{#ifexist:{{{5}}}| |[[Category:Articles with Template:About targeting a nonexistent page]]}}}}}}}}
</includeonly><noinclude>
{{Documentation}}
</noinclude>
p6vxn17ukqd26l71g7ynjaruvqjsyb3
281303
281302
2021-09-16T00:56:50Z
en>Wbm1058
0
should implement in [[Module:About]]. Piped {{!}} links causing false positives.
281303
wikitext
text/x-wiki
{{#invoke:about|about}}<noinclude>
{{Documentation}}
</noinclude>
65kia3vbcfrf851snuk172blbw5z7sh
281304
281303
2026-06-07T17:42:47Z
Ameisenigel
44
82 revisions imported from [[:en:Template:About]]: Request at [[WF:AN]]
281303
wikitext
text/x-wiki
{{#invoke:about|about}}<noinclude>
{{Documentation}}
</noinclude>
65kia3vbcfrf851snuk172blbw5z7sh
Module:About
828
85255
281305
2016-04-08T04:26:31Z
en>Nihiltres
0
A prototype…
281305
Scribunto
text/plain
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote/Sandbox') --sandbox since we use mHatnote._forSee(). Blocks removing from alpha.
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--A passthrough that gets args from the frame and all
function p.about (frame)
mArguments = require('Module:Arguments')
args = mArguments.getArgs(frame)
return p._about(args)
end
--Produces "about" hatnote. No options or defaults yet, but the machinery's there.
function p._about (args, options)
checkType('_about', 1, args, 'table', true)
args = args or {}
checkType('_about', 2, options, 'table', true)
options = options or {}
local defaultOptions = {
--whatever
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
local pageTypeStrings = {
[0] = 'article',
[14] = 'category'
}
local pageType = (args.section and 'section') or
pageTypeStrings[mw.title.getCurrentTitle().namespace] or
'page'
local about = ''
if args[1] then
about = string.format('This %s is about %s. ', pageType, args[1])
end
local forSee = mHatnote._forSee(args, 2)
return mHatnote._hatnote(about .. forSee)
end
return p
nfc8trkzdg4icew8s483dzvc5wigcxz
281306
281305
2016-04-08T04:28:00Z
en>Nihiltres
0
Case-sensitivity fail.
281306
Scribunto
text/plain
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote/sandbox') --sandbox since we use mHatnote._forSee(). Blocks removing from alpha.
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--A passthrough that gets args from the frame and all
function p.about (frame)
mArguments = require('Module:Arguments')
args = mArguments.getArgs(frame)
return p._about(args)
end
--Produces "about" hatnote. No options or defaults yet, but the machinery's there.
function p._about (args, options)
checkType('_about', 1, args, 'table', true)
args = args or {}
checkType('_about', 2, options, 'table', true)
options = options or {}
local defaultOptions = {
--whatever
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
local pageTypeStrings = {
[0] = 'article',
[14] = 'category'
}
local pageType = (args.section and 'section') or
pageTypeStrings[mw.title.getCurrentTitle().namespace] or
'page'
local about = ''
if args[1] then
about = string.format('This %s is about %s. ', pageType, args[1])
end
local forSee = mHatnote._forSee(args, 2)
return mHatnote._hatnote(about .. forSee)
end
return p
o431t6nunic75gdmkhxrbxooaydmjv1
281307
281306
2016-04-08T17:13:23Z
en>Nihiltres
0
Switched _forSee to new [[Module:Hatnote list]], added some options (migrated most strings from hard-coded to default options), and added comments
281307
Scribunto
text/plain
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local mHatList = require('Module:Hatnote list')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.about (frame)
-- A passthrough that gets args from the frame and all
mArguments = require('Module:Arguments')
args = mArguments.getArgs(frame)
return p._about(args)
end
function p._about (args, options)
-- Produces "about" hatnote.
-- Type checks and defaults
checkType('_about', 1, args, 'table', true)
args = args or {}
checkType('_about', 2, options, 'table', true)
options = options or {}
local defaultOptions = {
aboutForm = 'This %s is about %s. ',
defaultPageType = 'page',
pageTypeStrings = {
[0] = 'article',
[14] = 'category'
},
sectionString = 'section'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Set initial "about" string
local pageType = (args.section and options.sectionString) or
options.pageTypeStrings[mw.title.getCurrentTitle().namespace] or
options.defaultPageType
local about = ''
if args[1] then
about = string.format(options.aboutForm, pageType, args[1])
end
-- Set for-see list
local forSee = mHatList._forSee(args, 2)
-- Concatenate and return
return mHatnote._hatnote(about .. forSee)
end
return p
guidvbjzgn5ckewqyrzaaa2azqu20oi
281308
281307
2016-04-18T17:27:23Z
en>Nihiltres
0
Tweaked options to rename an option and add an option for specifying namespace instead of detecting it
281308
Scribunto
text/plain
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local mHatList = require('Module:Hatnote list')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.about (frame)
-- A passthrough that gets args from the frame and all
mArguments = require('Module:Arguments')
args = mArguments.getArgs(frame)
return p._about(args)
end
function p._about (args, options)
-- Produces "about" hatnote.
-- Type checks and defaults
checkType('_about', 1, args, 'table', true)
args = args or {}
checkType('_about', 2, options, 'table', true)
options = options or {}
local defaultOptions = {
aboutForm = 'This %s is about %s. ',
defaultPageType = 'page',
namespace = mw.title.getCurrentTitle().namespace,
pageTypesByNamespace = {
[0] = 'article',
[14] = 'category'
},
sectionString = 'section'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Set initial "about" string
local pageType = (args.section and options.sectionString) or
options.pageTypesByNamespace[options.namespace] or
options.defaultPageType
local about = ''
if args[1] then
about = string.format(options.aboutForm, pageType, args[1])
end
-- Set for-see list
local forSee = mHatList._forSee(args, 2)
-- Concatenate and return
return mHatnote._hatnote(about .. forSee)
end
return p
khe59w7tzmysjxezcg3vb1cwp6il1mx
281309
281308
2016-05-06T23:44:12Z
en>Nihiltres
0
Protected "[[Module:About]]": [[WP:High-risk templates|High-risk Lua module]]: Soon to be implemented in high-risk [[Template:About]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
281308
Scribunto
text/plain
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local mHatList = require('Module:Hatnote list')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.about (frame)
-- A passthrough that gets args from the frame and all
mArguments = require('Module:Arguments')
args = mArguments.getArgs(frame)
return p._about(args)
end
function p._about (args, options)
-- Produces "about" hatnote.
-- Type checks and defaults
checkType('_about', 1, args, 'table', true)
args = args or {}
checkType('_about', 2, options, 'table', true)
options = options or {}
local defaultOptions = {
aboutForm = 'This %s is about %s. ',
defaultPageType = 'page',
namespace = mw.title.getCurrentTitle().namespace,
pageTypesByNamespace = {
[0] = 'article',
[14] = 'category'
},
sectionString = 'section'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Set initial "about" string
local pageType = (args.section and options.sectionString) or
options.pageTypesByNamespace[options.namespace] or
options.defaultPageType
local about = ''
if args[1] then
about = string.format(options.aboutForm, pageType, args[1])
end
-- Set for-see list
local forSee = mHatList._forSee(args, 2)
-- Concatenate and return
return mHatnote._hatnote(about .. forSee)
end
return p
khe59w7tzmysjxezcg3vb1cwp6il1mx
281310
281309
2016-07-16T21:33:40Z
en>Nihiltres
0
Updated from sandbox with support for otherText option passthrough
281310
Scribunto
text/plain
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local mHatList = require('Module:Hatnote list')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.about (frame)
-- A passthrough that gets args from the frame and all
mArguments = require('Module:Arguments')
args = mArguments.getArgs(frame)
return p._about(args)
end
function p._about (args, options)
-- Produces "about" hatnote.
-- Type checks and defaults
checkType('_about', 1, args, 'table', true)
args = args or {}
checkType('_about', 2, options, 'table', true)
options = options or {}
local defaultOptions = {
aboutForm = 'This %s is about %s. ',
defaultPageType = 'page',
namespace = mw.title.getCurrentTitle().namespace,
otherText = nil, --included for complete list
pageTypesByNamespace = {
[0] = 'article',
[14] = 'category'
},
sectionString = 'section'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Set initial "about" string
local pageType = (args.section and options.sectionString) or
options.pageTypesByNamespace[options.namespace] or
options.defaultPageType
local about = ''
if args[1] then
about = string.format(options.aboutForm, pageType, args[1])
end
--Allow passing through certain options
local fsOptions = {
otherText = options.otherText
}
-- Set for-see list
local forSee = mHatList._forSee(args, 2, fsOptions)
-- Concatenate and return
return mHatnote._hatnote(about .. forSee)
end
return p
dljvn3yousnkg58p45o8yxrkev89jqt
281311
281310
2018-03-31T10:24:13Z
en>Galobtter
0
add selfref and "text=" options; use pagetype module for detecting namespace
281311
Scribunto
text/plain
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local mHatList = require('Module:Hatnote list')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.about (frame)
-- A passthrough that gets args from the frame and all
mArguments = require('Module:Arguments')
args = mArguments.getArgs(frame)
return p._about(args)
end
function p._about (args, options)
-- Produces "about" hatnote.
-- Type checks and defaults
checkType('_about', 1, args, 'table', true)
args = args or {}
checkType('_about', 2, options, 'table', true)
options = options or {}
local defaultOptions = {
aboutForm = 'This %s is about %s. ',
PageType = require('Module:Pagetype').main(),
namespace = mw.title.getCurrentTitle().namespace,
otherText = nil, --included for complete list
sectionString = 'section'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Set initial "about" string
local pageType = (args.section and options.sectionString) or options.PageType
local about = ''
if args[1] then
about = string.format(options.aboutForm, pageType, args[1])
end
--Allow passing through certain options
local fsOptions = {
otherText = options.otherText,
extratext = args.text
}
local hnOptions = {
selfref = args.selfref
}
-- Set for-see list
local forSee = mHatList._forSee(args, 2, fsOptions)
-- Concatenate and return
return mHatnote._hatnote(about .. forSee, hnOptions)
end
return p
sues5orgepkp4a2wup57qbry8xl812l
281312
281311
2020-07-14T13:59:23Z
en>Nihiltres
0
Removed defaultOptions.namespace as redundant, per request on talk by Andrybak
281312
Scribunto
text/plain
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local mHatList = require('Module:Hatnote list')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.about (frame)
-- A passthrough that gets args from the frame and all
mArguments = require('Module:Arguments')
args = mArguments.getArgs(frame)
return p._about(args)
end
function p._about (args, options)
-- Produces "about" hatnote.
-- Type checks and defaults
checkType('_about', 1, args, 'table', true)
args = args or {}
checkType('_about', 2, options, 'table', true)
options = options or {}
local defaultOptions = {
aboutForm = 'This %s is about %s. ',
PageType = require('Module:Pagetype').main(),
otherText = nil, --included for complete list
sectionString = 'section'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Set initial "about" string
local pageType = (args.section and options.sectionString) or options.PageType
local about = ''
if args[1] then
about = string.format(options.aboutForm, pageType, args[1])
end
--Allow passing through certain options
local fsOptions = {
otherText = options.otherText,
extratext = args.text
}
local hnOptions = {
selfref = args.selfref
}
-- Set for-see list
local forSee = mHatList._forSee(args, 2, fsOptions)
-- Concatenate and return
return mHatnote._hatnote(about .. forSee, hnOptions)
end
return p
34eqkrvgvpb6coyf5mc8amjmasfegyy
281313
281312
2026-06-07T17:44:01Z
Ameisenigel
44
8 revisions imported from [[:en:Module:About]]: Request at [[WF:AN]]
281312
Scribunto
text/plain
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local mHatList = require('Module:Hatnote list')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.about (frame)
-- A passthrough that gets args from the frame and all
mArguments = require('Module:Arguments')
args = mArguments.getArgs(frame)
return p._about(args)
end
function p._about (args, options)
-- Produces "about" hatnote.
-- Type checks and defaults
checkType('_about', 1, args, 'table', true)
args = args or {}
checkType('_about', 2, options, 'table', true)
options = options or {}
local defaultOptions = {
aboutForm = 'This %s is about %s. ',
PageType = require('Module:Pagetype').main(),
otherText = nil, --included for complete list
sectionString = 'section'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Set initial "about" string
local pageType = (args.section and options.sectionString) or options.PageType
local about = ''
if args[1] then
about = string.format(options.aboutForm, pageType, args[1])
end
--Allow passing through certain options
local fsOptions = {
otherText = options.otherText,
extratext = args.text
}
local hnOptions = {
selfref = args.selfref
}
-- Set for-see list
local forSee = mHatList._forSee(args, 2, fsOptions)
-- Concatenate and return
return mHatnote._hatnote(about .. forSee, hnOptions)
end
return p
34eqkrvgvpb6coyf5mc8amjmasfegyy
Module:Hatnote
828
85256
281314
2014-04-15T15:38:52Z
en>Mr. Stradivarius
0
make a start on a module for making hatnotes
281314
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
local namespace = link:match('^(.-):')
local colon
if namespace and mw.site.namespaces[namespace] then
-- The start of the link is a valid namespace name; check whether it is
-- a category or a file.
local nsid = mw.site.namespaces[namespace].id
if nsid == 6 or nsid == 14 then
colon = ':'
end
end
colon = colon or ''
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
return p
9iaiaa0i81spkre54e0y45llpxf5y0g
281315
281314
2014-04-15T16:01:33Z
en>Mr. Stradivarius
0
add [[Template:Details]]
281315
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
local namespace = link:match('^(.-):')
local colon
if namespace and mw.site.namespaces[namespace] then
-- The start of the link is a valid namespace name; check whether it is
-- a category or a file.
local nsid = mw.site.namespaces[namespace].id
if nsid == 6 or nsid == 14 then
colon = ':'
end
end
colon = colon or ''
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
return p
5wtlcc2gojx3t83016kuhlu802xnxb2
281316
281315
2014-04-15T16:16:25Z
en>Mr. Stradivarius
0
add [[Template:Further]]
281316
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
local namespace = link:match('^(.-):')
local colon
if namespace and mw.site.namespaces[namespace] then
-- The start of the link is a valid namespace name; check whether it is
-- a category or a file.
local nsid = mw.site.namespaces[namespace].id
if nsid == 6 or nsid == 14 then
colon = ':'
end
end
colon = colon or ''
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local pages = {...}
local formattedPages = {}
for i, page in ipairs(pages) do
formattedPages[i] = formatLink(page)
end
local text = 'Further information: ' .. mw.text.listToText(formattedPages)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikiTextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
return p
rghuc9ulr5c45z95q6stj8tfuit34vl
281317
281316
2014-04-15T16:37:37Z
en>Mr. Stradivarius
0
split out the namespace-detecting part of formatLink to a new function
281317
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function findNamespaceId(link)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets or if
-- the link has been escaped with the colon trick.
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
local namespace = findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local pages = {...}
local formattedPages = {}
for i, page in ipairs(pages) do
formattedPages[i] = formatLink(page)
end
local text = 'Further information: ' .. mw.text.listToText(formattedPages)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikiTextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
return p
qru65umlav5k5wtbj7homvvt95mazef
281318
281317
2014-04-15T18:02:20Z
en>Mr. Stradivarius
0
add [[Template:Main]]
281318
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function findNamespaceId(link)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets or if
-- the link has been escaped with the colon trick.
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
local namespace = findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = formatLink(page)
end
return ret
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local links = formatPages(...)
local text = 'Further information: ' .. mw.text.listToText(links)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikiTextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
--------------------------------------------------------------------------------
-- Main
--
-- Produces a link to a main article or articles. If used in category or
-- category talk space, produces "The main article for this category is xxx".
-- Otherwise, produces "Main article: xxx". Accepts an unlimited number of
-- positional parameters, each of which is a page name. If the first positional
-- parameter is not in mainspace, uses "page" instead of "article". If more
-- than one page is specified, the function uses plural forms.
--------------------------------------------------------------------------------
function p._main(args)
-- Initialize variables.
local links, firstPage
local currentTitle = mw.title.getCurrentTitle()
-- Make the list of formatted links and find the link for the first page.
local nums = mTableTools.numKeys(args)
if nums[1] then
firstPage = args[nums[1]]
links = {}
else
firstPage = currentTitle.text
links = {formatLink(firstPage)}
end
for i, num in ipairs(nums) do
local link = args[num]
local display = args['l' .. tostring(num)]
links[#links + 1] = formatLink(link, display)
end
-- Find the pagetype.
local firstPageNs = findNamespaceId(firstPage)
local pagetype = firstPageNs == 0 and 'article' or 'page'
-- Build the text.
local isPlural = #links > 1
local currentNs = currentTitle.namespace
local isCategoryNamespace = currentNs - currentNs % 2 == 14
links = mw.text.listToText(links)
local stringToFormat
if isCategoryNamespace then
if isPlural then
stringToFormat = 'The main %ss for this'
.. ' [[Wikipedia:Categorization|category]] are %s'
else
stringToFormat = 'The main %s for this'
.. ' [[Wikipedia:Categorization|category]] is %s'
end
else
if isPlural then
stringToFormat = 'Main %ss: %s'
else
stringToFormat = 'Main %s: %s'
end
end
local text = string.format(stringToFormat, pagetype, links)
-- Pass the text to p._rellink.
local extraclasses = 'relarticle mainarticle'
return p._rellink(text, extraclasses)
end
p.main = makeInvokeFunction(p._main)
return p
0scx8upzlgras8qidm9hnhw3pxxe0o6
281319
281318
2014-04-15T18:08:00Z
en>Mr. Stradivarius
0
update main function description
281319
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function findNamespaceId(link)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets or if
-- the link has been escaped with the colon trick.
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
local namespace = findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = formatLink(page)
end
return ret
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local links = formatPages(...)
local text = 'Further information: ' .. mw.text.listToText(links)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikiTextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
--------------------------------------------------------------------------------
-- Main
--
-- Produces a link to a main article or articles. If used in category or
-- category talk space, produces "The main article for this category is xxx".
-- Otherwise, produces "Main article: xxx". Accepts a table of arguments.
-- Numerical keys for this table are the page names. If the first page name is
-- not in mainspace, uses "page" instead of "article". If more than one page is
-- specified, the function uses plural forms. Display names can be specified for
-- each page name by using the arguments l1, l2, etc.
--------------------------------------------------------------------------------
function p._main(args)
-- Initialize variables.
local links, firstPage
local currentTitle = mw.title.getCurrentTitle()
-- Make the list of formatted links and find the link for the first page.
local nums = mTableTools.numKeys(args)
if nums[1] then
firstPage = args[nums[1]]
links = {}
else
firstPage = currentTitle.text
links = {formatLink(firstPage)}
end
for i, num in ipairs(nums) do
local link = args[num]
local display = args['l' .. tostring(num)]
links[#links + 1] = formatLink(link, display)
end
-- Find the pagetype.
local firstPageNs = findNamespaceId(firstPage)
local pagetype = firstPageNs == 0 and 'article' or 'page'
-- Build the text.
local isPlural = #links > 1
local currentNs = currentTitle.namespace
local isCategoryNamespace = currentNs - currentNs % 2 == 14
links = mw.text.listToText(links)
local stringToFormat
if isCategoryNamespace then
if isPlural then
stringToFormat = 'The main %ss for this'
.. ' [[Wikipedia:Categorization|category]] are %s'
else
stringToFormat = 'The main %s for this'
.. ' [[Wikipedia:Categorization|category]] is %s'
end
else
if isPlural then
stringToFormat = 'Main %ss: %s'
else
stringToFormat = 'Main %s: %s'
end
end
local text = string.format(stringToFormat, pagetype, links)
-- Pass the text to p._rellink.
local extraclasses = 'relarticle mainarticle'
return p._rellink(text, extraclasses)
end
p.main = makeInvokeFunction(p._main)
return p
jhqdc2obwqt9vwbecmrk4ic4ntnuwxv
281320
281319
2014-04-15T18:45:24Z
en>Mr. Stradivarius
0
format p._main to accept a list of page/display tables as input
281320
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function findNamespaceId(link)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets or if
-- the link has been escaped with the colon trick.
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
local namespace = findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = formatLink(page)
end
return ret
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local links = formatPages(...)
local text = 'Further information: ' .. mw.text.listToText(links)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikiTextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
--------------------------------------------------------------------------------
-- Main
--
-- Produces a link to a main article or articles. If used in category or
-- category talk space, produces "The main article for this category is xxx".
-- Otherwise, produces "Main article: xxx". Accepts an unlimited number of
-- page/display tables. Non-table inputs will result in an error. The first
-- value in the table should be the page name. Omitting this will result in an
-- error, except in the case of the first table, which uses the page name as a
-- fallaback. The second value in the table is an optional display value for
-- the link. If the first page name is not in mainspace, the output uses "page"
-- instead of "article". If more than one page is specified, the function uses
-- plural forms.
--------------------------------------------------------------------------------
function p._main(...)
-- Get the list of pages. If no first page was specified we use the current
-- page name.
local pages = {...}
local currentTitle = mw.title.getCurrentTitle()
local firstPageTable = pages[1]
local firstPage
if firstPageTable then
firstPage = firstPageTable[1]
else
firstPage = currentTitle.text
firstPageTable = {firstPage}
pages[1] = firstPageTable
end
-- Make the list of formatted links
local links = {}
for i, t in ipairs(pages) do
local link = t[1]
local display = t[2]
links[#links + 1] = formatLink(link, display)
end
-- Find the pagetype.
local firstPageNs = findNamespaceId(firstPage)
local pagetype = firstPageNs == 0 and 'article' or 'page'
-- Build the text.
local isPlural = #links > 1
local currentNs = currentTitle.namespace
local isCategoryNamespace = currentNs - currentNs % 2 == 14
links = mw.text.listToText(links)
local stringToFormat
if isCategoryNamespace then
if isPlural then
stringToFormat = 'The main %ss for this'
.. ' [[Wikipedia:Categorization|category]] are %s'
else
stringToFormat = 'The main %s for this'
.. ' [[Wikipedia:Categorization|category]] is %s'
end
else
if isPlural then
stringToFormat = 'Main %ss: %s'
else
stringToFormat = 'Main %s: %s'
end
end
local text = string.format(stringToFormat, pagetype, links)
-- Pass the text to p._rellink.
local extraclasses = 'relarticle mainarticle'
return p._rellink(text, extraclasses)
end
function f.main(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local display = args['l' .. tostring(k)]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
return p._main(unpack(pages))
end
p.main = makeInvokeFunction(f.main)
return p
a136u0awke56lgt22erg8ks6e47r8ky
281321
281320
2014-04-15T19:06:58Z
en>Mr. Stradivarius
0
factor out some of the main code to a shared function
281321
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function findNamespaceId(link)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets or if
-- the link has been escaped with the colon trick.
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
local namespace = findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = formatLink(page)
end
return ret
end
local function formatPageTables(pages)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local links = {}
for i, t in ipairs(pages) do
local link = t[1]
local display = t[2]
links[i] = formatLink(link, display)
end
return links
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local links = formatPages(...)
local text = 'Further information: ' .. mw.text.listToText(links)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikiTextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
--------------------------------------------------------------------------------
-- Main
--
-- Produces a link to a main article or articles. If used in category or
-- category talk space, produces "The main article for this category is xxx".
-- Otherwise, produces "Main article: xxx". Accepts an unlimited number of
-- page/display tables. Non-table inputs will result in an error. The first
-- value in the table should be the page name. Omitting this will result in an
-- error, except in the case of the first table, which uses the page name as a
-- fallaback. The second value in the table is an optional display value for
-- the link. If the first page name is not in mainspace, the output uses "page"
-- instead of "article". If more than one page is specified, the function uses
-- plural forms.
--------------------------------------------------------------------------------
function p._main(...)
-- Get the list of pages. If no first page was specified we use the current
-- page name.
local pages = {...}
local currentTitle = mw.title.getCurrentTitle()
local firstPageTable = pages[1]
local firstPage
if firstPageTable then
firstPage = firstPageTable[1]
else
firstPage = currentTitle.text
firstPageTable = {firstPage}
pages[1] = firstPageTable
end
-- Find the pagetype.
local firstPageNs = findNamespaceId(firstPage)
local pagetype = firstPageNs == 0 and 'article' or 'page'
-- Make the formatted link text
local links = formatPageTables(pages)
links = mw.text.listToText(links)
-- Build the text.
local isPlural = #pages > 1
local currentNs = currentTitle.namespace
local isCategoryNamespace = currentNs - currentNs % 2 == 14
local stringToFormat
if isCategoryNamespace then
if isPlural then
stringToFormat = 'The main %ss for this'
.. ' [[Wikipedia:Categorization|category]] are %s'
else
stringToFormat = 'The main %s for this'
.. ' [[Wikipedia:Categorization|category]] is %s'
end
else
if isPlural then
stringToFormat = 'Main %ss: %s'
else
stringToFormat = 'Main %s: %s'
end
end
local text = string.format(stringToFormat, pagetype, links)
-- Pass the text to p._rellink.
local extraclasses = 'relarticle mainarticle'
return p._rellink(text, extraclasses)
end
function f.main(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local display = args['l' .. tostring(k)]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
return p._main(unpack(pages))
end
p.main = makeInvokeFunction(f.main)
return p
0gynvccghg2n5znn385mwp7thl8r0rb
281322
281321
2014-04-15T19:18:20Z
en>Mr. Stradivarius
0
add [[Template:See also]]
281322
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function findNamespaceId(link)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets or if
-- the link has been escaped with the colon trick.
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
local namespace = findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = formatLink(page)
end
return ret
end
local function formatPageTables(pages)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local links = {}
for i, t in ipairs(pages) do
local link = t[1]
local display = t[2]
links[i] = formatLink(link, display)
end
return links
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local links = formatPages(...)
local text = 'Further information: ' .. mw.text.listToText(links)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikiTextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
--------------------------------------------------------------------------------
-- Main
--
-- Produces a link to a main article or articles. If used in category or
-- category talk space, produces "The main article for this category is xxx".
-- Otherwise, produces "Main article: xxx". Accepts an unlimited number of
-- page/display tables. Non-table inputs will result in an error. The first
-- value in the table should be the page name. Omitting this will result in an
-- error, except in the case of the first table, which uses the page name as a
-- fallaback. The second value in the table is an optional display value for
-- the link. If the first page name is not in mainspace, the output uses "page"
-- instead of "article". If more than one page is specified, the function uses
-- plural forms.
--------------------------------------------------------------------------------
function p._main(...)
-- Get the list of pages. If no first page was specified we use the current
-- page name.
local pages = {...}
local currentTitle = mw.title.getCurrentTitle()
local firstPageTable = pages[1]
local firstPage
if firstPageTable then
firstPage = firstPageTable[1]
else
firstPage = currentTitle.text
firstPageTable = {firstPage}
pages[1] = firstPageTable
end
-- Find the pagetype.
local firstPageNs = findNamespaceId(firstPage)
local pagetype = firstPageNs == 0 and 'article' or 'page'
-- Make the formatted link text
local links = formatPageTables(pages)
links = mw.text.listToText(links)
-- Build the text.
local isPlural = #pages > 1
local currentNs = currentTitle.namespace
local isCategoryNamespace = currentNs - currentNs % 2 == 14
local stringToFormat
if isCategoryNamespace then
if isPlural then
stringToFormat = 'The main %ss for this'
.. ' [[Wikipedia:Categorization|category]] are %s'
else
stringToFormat = 'The main %s for this'
.. ' [[Wikipedia:Categorization|category]] is %s'
end
else
if isPlural then
stringToFormat = 'Main %ss: %s'
else
stringToFormat = 'Main %s: %s'
end
end
local text = string.format(stringToFormat, pagetype, links)
-- Pass the text to p._rellink.
local extraclasses = 'relarticle mainarticle'
return p._rellink(text, extraclasses)
end
function f.main(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local display = args['l' .. tostring(k)]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
return p._main(unpack(pages))
end
p.main = makeInvokeFunction(f.main)
--------------------------------------------------------------------------------
-- See also
--
-- Produces a "See also: a, b and c" link. The first parameter is an optional
-- alternative for the "See also" text. The following parameters are an
-- unlimited number of page/display tables. The first entry in the table is the
-- page name, and the second entry in the table is the display text.
--------------------------------------------------------------------------------
function p._seealso(altphrase, ...)
altphrase = altphrase or 'See also'
local pages = {...}
local links = formatPageTables(pages)
links = mw.text.listToText(links)
local text = altphrase .. ': ' .. links
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.seealso(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local numstring = tostring(k)
local display = args['label ' .. numstring]
or args['l' .. numstring]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
if not pages[1] then
return makeWikitextError(
'[[Template:See also|'
.. 'Template must be given at least one article name]]'
)
end
local altphrase = args.altphrase
return p._seealso(altphrase, unpack(pages))
end
p.seealso = makeInvokeFunction(f.seealso)
return p
ttebi5fyk6fwwnpqcuf324seaodwbkt
281323
281322
2014-04-16T09:13:58Z
en>Mr. Stradivarius
0
properly deal with links escaped using the colon trick
281323
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function findNamespaceId(link)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets or if
-- the link has been escaped with the colon trick.
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
link = link:match('^:?(.*)') -- Remove initial colon if specified.
local namespace = findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = formatLink(page)
end
return ret
end
local function formatPageTables(pages)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local links = {}
for i, t in ipairs(pages) do
local link = t[1]
local display = t[2]
links[i] = formatLink(link, display)
end
return links
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local links = formatPages(...)
local text = 'Further information: ' .. mw.text.listToText(links)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikiTextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
--------------------------------------------------------------------------------
-- Main
--
-- Produces a link to a main article or articles. If used in category or
-- category talk space, produces "The main article for this category is xxx".
-- Otherwise, produces "Main article: xxx". Accepts an unlimited number of
-- page/display tables. Non-table inputs will result in an error. The first
-- value in the table should be the page name. Omitting this will result in an
-- error, except in the case of the first table, which uses the page name as a
-- fallaback. The second value in the table is an optional display value for
-- the link. If the first page name is not in mainspace, the output uses "page"
-- instead of "article". If more than one page is specified, the function uses
-- plural forms.
--------------------------------------------------------------------------------
function p._main(...)
-- Get the list of pages. If no first page was specified we use the current
-- page name.
local pages = {...}
local currentTitle = mw.title.getCurrentTitle()
local firstPageTable = pages[1]
local firstPage
if firstPageTable then
firstPage = firstPageTable[1]
else
firstPage = currentTitle.text
firstPageTable = {firstPage}
pages[1] = firstPageTable
end
-- Find the pagetype.
local firstPageNs = findNamespaceId(firstPage)
local pagetype = firstPageNs == 0 and 'article' or 'page'
-- Make the formatted link text
local links = formatPageTables(pages)
links = mw.text.listToText(links)
-- Build the text.
local isPlural = #pages > 1
local currentNs = currentTitle.namespace
local isCategoryNamespace = currentNs - currentNs % 2 == 14
local stringToFormat
if isCategoryNamespace then
if isPlural then
stringToFormat = 'The main %ss for this'
.. ' [[Wikipedia:Categorization|category]] are %s'
else
stringToFormat = 'The main %s for this'
.. ' [[Wikipedia:Categorization|category]] is %s'
end
else
if isPlural then
stringToFormat = 'Main %ss: %s'
else
stringToFormat = 'Main %s: %s'
end
end
local text = string.format(stringToFormat, pagetype, links)
-- Pass the text to p._rellink.
local extraclasses = 'relarticle mainarticle'
return p._rellink(text, extraclasses)
end
function f.main(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local display = args['l' .. tostring(k)]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
return p._main(unpack(pages))
end
p.main = makeInvokeFunction(f.main)
--------------------------------------------------------------------------------
-- See also
--
-- Produces a "See also: a, b and c" link. The first parameter is an optional
-- alternative for the "See also" text. The following parameters are an
-- unlimited number of page/display tables. The first entry in the table is the
-- page name, and the second entry in the table is the display text.
--------------------------------------------------------------------------------
function p._seealso(altphrase, ...)
altphrase = altphrase or 'See also'
local pages = {...}
local links = formatPageTables(pages)
links = mw.text.listToText(links)
local text = altphrase .. ': ' .. links
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.seealso(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local numstring = tostring(k)
local display = args['label ' .. numstring]
or args['l' .. numstring]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
if not pages[1] then
return makeWikitextError(
'[[Template:See also|'
.. 'Template must be given at least one article name]]'
)
end
local altphrase = args.altphrase
return p._seealso(altphrase, unpack(pages))
end
p.seealso = makeInvokeFunction(f.seealso)
return p
7f9mo6oejb6jp31gfaav6emdmf81a9x
281324
281323
2014-04-16T09:17:12Z
en>Mr. Stradivarius
0
fix colon-removing pattern
281324
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function findNamespaceId(link)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets or if
-- the link has been escaped with the colon trick.
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
link = link:match('^:(.*)') or link -- Remove initial colon if specified.
local namespace = findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = formatLink(page)
end
return ret
end
local function formatPageTables(pages)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local links = {}
for i, t in ipairs(pages) do
local link = t[1]
local display = t[2]
links[i] = formatLink(link, display)
end
return links
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local links = formatPages(...)
local text = 'Further information: ' .. mw.text.listToText(links)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikiTextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
--------------------------------------------------------------------------------
-- Main
--
-- Produces a link to a main article or articles. If used in category or
-- category talk space, produces "The main article for this category is xxx".
-- Otherwise, produces "Main article: xxx". Accepts an unlimited number of
-- page/display tables. Non-table inputs will result in an error. The first
-- value in the table should be the page name. Omitting this will result in an
-- error, except in the case of the first table, which uses the page name as a
-- fallaback. The second value in the table is an optional display value for
-- the link. If the first page name is not in mainspace, the output uses "page"
-- instead of "article". If more than one page is specified, the function uses
-- plural forms.
--------------------------------------------------------------------------------
function p._main(...)
-- Get the list of pages. If no first page was specified we use the current
-- page name.
local pages = {...}
local currentTitle = mw.title.getCurrentTitle()
local firstPageTable = pages[1]
local firstPage
if firstPageTable then
firstPage = firstPageTable[1]
else
firstPage = currentTitle.text
firstPageTable = {firstPage}
pages[1] = firstPageTable
end
-- Find the pagetype.
local firstPageNs = findNamespaceId(firstPage)
local pagetype = firstPageNs == 0 and 'article' or 'page'
-- Make the formatted link text
local links = formatPageTables(pages)
links = mw.text.listToText(links)
-- Build the text.
local isPlural = #pages > 1
local currentNs = currentTitle.namespace
local isCategoryNamespace = currentNs - currentNs % 2 == 14
local stringToFormat
if isCategoryNamespace then
if isPlural then
stringToFormat = 'The main %ss for this'
.. ' [[Wikipedia:Categorization|category]] are %s'
else
stringToFormat = 'The main %s for this'
.. ' [[Wikipedia:Categorization|category]] is %s'
end
else
if isPlural then
stringToFormat = 'Main %ss: %s'
else
stringToFormat = 'Main %s: %s'
end
end
local text = string.format(stringToFormat, pagetype, links)
-- Pass the text to p._rellink.
local extraclasses = 'relarticle mainarticle'
return p._rellink(text, extraclasses)
end
function f.main(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local display = args['l' .. tostring(k)]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
return p._main(unpack(pages))
end
p.main = makeInvokeFunction(f.main)
--------------------------------------------------------------------------------
-- See also
--
-- Produces a "See also: a, b and c" link. The first parameter is an optional
-- alternative for the "See also" text. The following parameters are an
-- unlimited number of page/display tables. The first entry in the table is the
-- page name, and the second entry in the table is the display text.
--------------------------------------------------------------------------------
function p._seealso(altphrase, ...)
altphrase = altphrase or 'See also'
local pages = {...}
local links = formatPageTables(pages)
links = mw.text.listToText(links)
local text = altphrase .. ': ' .. links
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.seealso(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local numstring = tostring(k)
local display = args['label ' .. numstring]
or args['l' .. numstring]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
if not pages[1] then
return makeWikitextError(
'[[Template:See also|'
.. 'Template must be given at least one article name]]'
)
end
local altphrase = args.altphrase
return p._seealso(altphrase, unpack(pages))
end
p.seealso = makeInvokeFunction(f.seealso)
return p
gc6kadswhcoy28mmzh5lw3ig70jn8x2
281325
281324
2014-04-16T09:43:28Z
en>Mr. Stradivarius
0
prevent categories escaped with the colon trick being displayed as "article"
281325
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. If the
-- removeColon parameter is set to true, the function will remove initial
-- colons from the link.
if removeColon then
link = link:match('^:?(.*)')
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
link = link:match('^:?(.*)') -- Remove initial colon if specified.
local namespace = findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = formatLink(page)
end
return ret
end
local function formatPageTables(pages)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local links = {}
for i, t in ipairs(pages) do
local link = t[1]
local display = t[2]
links[i] = formatLink(link, display)
end
return links
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local links = formatPages(...)
local text = 'Further information: ' .. mw.text.listToText(links)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikiTextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
--------------------------------------------------------------------------------
-- Main
--
-- Produces a link to a main article or articles. If used in category or
-- category talk space, produces "The main article for this category is xxx".
-- Otherwise, produces "Main article: xxx". Accepts an unlimited number of
-- page/display tables. Non-table inputs will result in an error. The first
-- value in the table should be the page name. Omitting this will result in an
-- error, except in the case of the first table, which uses the page name as a
-- fallaback. The second value in the table is an optional display value for
-- the link. If the first page name is not in mainspace, the output uses "page"
-- instead of "article". If more than one page is specified, the function uses
-- plural forms.
--------------------------------------------------------------------------------
function p._main(...)
-- Get the list of pages. If no first page was specified we use the current
-- page name.
local pages = {...}
local currentTitle = mw.title.getCurrentTitle()
local firstPageTable = pages[1]
local firstPage
if firstPageTable then
firstPage = firstPageTable[1]
else
firstPage = currentTitle.text
firstPageTable = {firstPage}
pages[1] = firstPageTable
end
-- Find the pagetype.
local firstPageNs = findNamespaceId(firstPage, true)
local pagetype = firstPageNs == 0 and 'article' or 'page'
-- Make the formatted link text
local links = formatPageTables(pages)
links = mw.text.listToText(links)
-- Build the text.
local isPlural = #pages > 1
local currentNs = currentTitle.namespace
local isCategoryNamespace = currentNs - currentNs % 2 == 14
local stringToFormat
if isCategoryNamespace then
if isPlural then
stringToFormat = 'The main %ss for this'
.. ' [[Wikipedia:Categorization|category]] are %s'
else
stringToFormat = 'The main %s for this'
.. ' [[Wikipedia:Categorization|category]] is %s'
end
else
if isPlural then
stringToFormat = 'Main %ss: %s'
else
stringToFormat = 'Main %s: %s'
end
end
local text = string.format(stringToFormat, pagetype, links)
-- Pass the text to p._rellink.
local extraclasses = 'relarticle mainarticle'
return p._rellink(text, extraclasses)
end
function f.main(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local display = args['l' .. tostring(k)]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
return p._main(unpack(pages))
end
p.main = makeInvokeFunction(f.main)
--------------------------------------------------------------------------------
-- See also
--
-- Produces a "See also: a, b and c" link. The first parameter is an optional
-- alternative for the "See also" text. The following parameters are an
-- unlimited number of page/display tables. The first entry in the table is the
-- page name, and the second entry in the table is the display text.
--------------------------------------------------------------------------------
function p._seealso(altphrase, ...)
altphrase = altphrase or 'See also'
local pages = {...}
local links = formatPageTables(pages)
links = mw.text.listToText(links)
local text = altphrase .. ': ' .. links
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.seealso(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local numstring = tostring(k)
local display = args['label ' .. numstring]
or args['l' .. numstring]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
if not pages[1] then
return makeWikitextError(
'[[Template:See also|'
.. 'Template must be given at least one article name]]'
)
end
local altphrase = args.altphrase
return p._seealso(altphrase, unpack(pages))
end
p.seealso = makeInvokeFunction(f.seealso)
return p
jkuvtpxwxmpn0qwq2haqlg6x4tqa46m
281326
281325
2014-04-16T09:55:45Z
en>Mr. Stradivarius
0
fix typo in function name
281326
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and a few of --
-- the more popular templates they depend on, including {{main}}, --
-- {{see also}}, {{further}} and {{details}}. --
--------------------------------------------------------------------------------
local mTableTools = require('Module:TableTools')
local mArguments = require('Module:Arguments')
--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------
--[[
-- The p table is for functions to be returned from #invoke, and for functions
-- to be used from other Lua modules. The f table is for functions acting as a
-- bridge between #invoke functions and Lua module functions. #invoke functions
-- are connected to f table functions through the makeInvokeFunction function.
-- Functions for use from other Lua modules have names beginning with an
-- underscore.
--]]
local p, f = {}, {}
local function makeInvokeFunction(func)
return function(frame)
local args = mArguments.getArgs(frame, {parentOnly = true})
return func(args)
end
end
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. If the
-- removeColon parameter is set to true, the function will remove initial
-- colons from the link.
if removeColon then
link = link:match('^:?(.*)')
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
local function formatLink(link, display)
-- Makes a wikilink from the given link and display values. Links are
-- escaped with colons if necessary, and links to sections are detected
-- and displayed with " § " as a separator rather than the standard
-- MediaWiki "#".
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
link = link:match('^:?(.*)') -- Remove initial colon if specified.
local namespace = findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
local function formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = formatLink(page)
end
return ret
end
local function formatPageTables(pages)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local links = {}
for i, t in ipairs(pages) do
local link = t[1]
local display = t[2]
links[i] = formatLink(link, display)
end
return links
end
local function makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p._hatnote(s)
return string.format('<div class="dablink">%s</div>', s)
end
function f.hatnote(args)
local s = args[1]
if not s then
return makeWikitextError('no text specified')
end
return p._hatnote(s)
end
p.hatnote = makeInvokeFunction(f.hatnote)
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p._rellink(s, extraclasses)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
function f.rellink(args)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
p.rellink = makeInvokeFunction(f.rellink)
--------------------------------------------------------------------------------
-- Details
--
-- Produces a "For more details on this topic" link. the first parameter is the
-- page linked to, and if the second parameter is present it is used instead
-- of the "this topic" text.
--------------------------------------------------------------------------------
function p._details(page, topic)
page = formatLink(page)
topic = topic or 'this topic'
local text = string.format('For more details on %s, see %s.', topic, page)
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.details(args)
local page = args[1]
local topic = args[2]
if not page then
return makeWikitextError('no page specified')
end
return p._details(page, topic)
end
p.details = makeInvokeFunction(f.details)
--------------------------------------------------------------------------------
-- Further
--
-- Produces a "Further information: a, b and c" link. It accepts an unlimited
-- number of positional parameters, each of which is a page name.
--------------------------------------------------------------------------------
function p._further(...)
local links = formatPages(...)
local text = 'Further information: ' .. mw.text.listToText(links)
return p._rellink(text)
end
function f.further(args)
local pages = mTableTools.compressSparseArray(args)
if #pages < 1 then
return makeWikitextError('no pages specified')
end
return p._further(unpack(pages))
end
p.further = makeInvokeFunction(f.further)
--------------------------------------------------------------------------------
-- Main
--
-- Produces a link to a main article or articles. If used in category or
-- category talk space, produces "The main article for this category is xxx".
-- Otherwise, produces "Main article: xxx". Accepts an unlimited number of
-- page/display tables. Non-table inputs will result in an error. The first
-- value in the table should be the page name. Omitting this will result in an
-- error, except in the case of the first table, which uses the page name as a
-- fallaback. The second value in the table is an optional display value for
-- the link. If the first page name is not in mainspace, the output uses "page"
-- instead of "article". If more than one page is specified, the function uses
-- plural forms.
--------------------------------------------------------------------------------
function p._main(...)
-- Get the list of pages. If no first page was specified we use the current
-- page name.
local pages = {...}
local currentTitle = mw.title.getCurrentTitle()
local firstPageTable = pages[1]
local firstPage
if firstPageTable then
firstPage = firstPageTable[1]
else
firstPage = currentTitle.text
firstPageTable = {firstPage}
pages[1] = firstPageTable
end
-- Find the pagetype.
local firstPageNs = findNamespaceId(firstPage, true)
local pagetype = firstPageNs == 0 and 'article' or 'page'
-- Make the formatted link text
local links = formatPageTables(pages)
links = mw.text.listToText(links)
-- Build the text.
local isPlural = #pages > 1
local currentNs = currentTitle.namespace
local isCategoryNamespace = currentNs - currentNs % 2 == 14
local stringToFormat
if isCategoryNamespace then
if isPlural then
stringToFormat = 'The main %ss for this'
.. ' [[Wikipedia:Categorization|category]] are %s'
else
stringToFormat = 'The main %s for this'
.. ' [[Wikipedia:Categorization|category]] is %s'
end
else
if isPlural then
stringToFormat = 'Main %ss: %s'
else
stringToFormat = 'Main %s: %s'
end
end
local text = string.format(stringToFormat, pagetype, links)
-- Pass the text to p._rellink.
local extraclasses = 'relarticle mainarticle'
return p._rellink(text, extraclasses)
end
function f.main(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local display = args['l' .. tostring(k)]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
return p._main(unpack(pages))
end
p.main = makeInvokeFunction(f.main)
--------------------------------------------------------------------------------
-- See also
--
-- Produces a "See also: a, b and c" link. The first parameter is an optional
-- alternative for the "See also" text. The following parameters are an
-- unlimited number of page/display tables. The first entry in the table is the
-- page name, and the second entry in the table is the display text.
--------------------------------------------------------------------------------
function p._seealso(altphrase, ...)
altphrase = altphrase or 'See also'
local pages = {...}
local links = formatPageTables(pages)
links = mw.text.listToText(links)
local text = altphrase .. ': ' .. links
local extraclasses = 'boilerplate seealso'
return p._rellink(text, extraclasses)
end
function f.seealso(args)
local pages = {}
for k, v in pairs(args) do
if type(k) == 'number' then
local numstring = tostring(k)
local display = args['label ' .. numstring]
or args['l' .. numstring]
local page = {v, display}
pages[k] = page
end
end
pages = mTableTools.compressSparseArray(pages)
if not pages[1] then
return makeWikitextError(
'[[Template:See also|'
.. 'Template must be given at least one article name]]'
)
end
local altphrase = args.altphrase
return p._seealso(altphrase, unpack(pages))
end
p.seealso = makeInvokeFunction(f.seealso)
return p
s97wfazhvz4vq2lia07ypzofa5hk2s5
281327
281326
2014-04-24T07:11:28Z
en>Mr. Stradivarius
0
split main, see also, further and details out into their own templates, make formatLink available from #invoke, make other helper functions available from other Lua modules, and add type checks
281327
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p._findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. If the
-- removeColon parameter is set to true, the function will remove initial
-- colons from the link.
checkType('_findNamespaceId', 1, link, 'string')
checkType('_findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p._formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p._formatPageTables(pages)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
checkType('_formatPageTables', 1, pages, 'table')
local links = {}
for i, t in ipairs(pages) do
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p._makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
checkType('_makeWikitextError', 1, msg, 'string')
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p._makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p._findNamespaceId(link)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p._makeWikitextError('no text specified')
end
return p._hatnote(s)
end
function p._hatnote(s)
checkType('_hatnote', 1, s, 'string')
return string.format('<div class="dablink">%s</div>', s)
end
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p.rellink(frame)
local args = getArgs(frame)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return p._makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
function p._rellink(s, extraclasses)
checkType('_rellink', 1, s, 'string')
checkType('_rellink', 2, extraclasses, 'string', true)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
return p
p0gw3onmlx5h0wueuhbn8aw8wwtx8m9
281328
281327
2014-04-24T12:24:22Z
en>Mr. Stradivarius
0
trim colons in findNamespaceId by default
281328
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p._findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('_findNamespaceId', 1, link, 'string')
checkType('_findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p._formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p._formatPageTables(pages)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
checkType('_formatPageTables', 1, pages, 'table')
local links = {}
for i, t in ipairs(pages) do
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p._makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
checkType('_makeWikitextError', 1, msg, 'string')
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p._makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p._findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p._makeWikitextError('no text specified')
end
return p._hatnote(s)
end
function p._hatnote(s)
checkType('_hatnote', 1, s, 'string')
return string.format('<div class="dablink">%s</div>', s)
end
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p.rellink(frame)
local args = getArgs(frame)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return p._makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
function p._rellink(s, extraclasses)
checkType('_rellink', 1, s, 'string')
checkType('_rellink', 2, extraclasses, 'string', true)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
return p
ot1t9lxosyhxi34mm1z50vfgxov83xm
281329
281328
2014-04-24T12:44:38Z
en>Mr. Stradivarius
0
change format of _formatPageTables
281329
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p._findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('_findNamespaceId', 1, link, 'string')
checkType('_findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p._formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p._formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p._makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
checkType('_makeWikitextError', 1, msg, 'string')
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p._makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p._findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p._makeWikitextError('no text specified')
end
return p._hatnote(s)
end
function p._hatnote(s)
checkType('_hatnote', 1, s, 'string')
return string.format('<div class="dablink">%s</div>', s)
end
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p.rellink(frame)
local args = getArgs(frame)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return p._makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
function p._rellink(s, extraclasses)
checkType('_rellink', 1, s, 'string')
checkType('_rellink', 2, extraclasses, 'string', true)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
return p
9953ot7olnjn8xeyggmevbpywacbv76
281330
281329
2014-04-24T12:56:23Z
en>Mr. Stradivarius
0
add error check to _formatPageTables
281330
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p._findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('_findNamespaceId', 1, link, 'string')
checkType('_findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p._formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p._formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
if type(t) ~= 'table' then
error(string.format(
"bad argument #%d to '_formatPageTables' (table expected, got %s)",
i,
type(t)
), 2)
end
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p._makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
checkType('_makeWikitextError', 1, msg, 'string')
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p._makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p._findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p._makeWikitextError('no text specified')
end
return p._hatnote(s)
end
function p._hatnote(s)
checkType('_hatnote', 1, s, 'string')
return string.format('<div class="dablink">%s</div>', s)
end
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p.rellink(frame)
local args = getArgs(frame)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return p._makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
function p._rellink(s, extraclasses)
checkType('_rellink', 1, s, 'string')
checkType('_rellink', 2, extraclasses, 'string', true)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
return p
qwig5g226nfbnyfu9e3pdb0yfn0axfh
281331
281330
2014-04-26T08:43:49Z
en>Mr. Stradivarius
0
checkType will work in for loops as well, with the power of the i variable
281331
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p._findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('_findNamespaceId', 1, link, 'string')
checkType('_findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p._formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p._formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('_formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p._makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
checkType('_makeWikitextError', 1, msg, 'string')
return string.format('<strong class="error">Error: %s.</strong>', msg)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p._makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p._findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p._makeWikitextError('no text specified')
end
return p._hatnote(s)
end
function p._hatnote(s)
checkType('_hatnote', 1, s, 'string')
return string.format('<div class="dablink">%s</div>', s)
end
--------------------------------------------------------------------------------
-- Rellink
--
-- Produces a standard link to a related article. Implements the {{rellink}}
-- template.
--------------------------------------------------------------------------------
function p.rellink(frame)
local args = getArgs(frame)
local s = args[1]
local extraclasses = args.extraclasses
if not s then
return p._makeWikitextError('no text specified')
end
return p._rellink(s, extraclasses)
end
function p._rellink(s, extraclasses)
checkType('_rellink', 1, s, 'string')
checkType('_rellink', 2, extraclasses, 'string', true)
if extraclasses then
extraclasses = ' ' .. extraclasses
else
extraclasses = ''
end
return string.format('<div class="rellink%s">%s</div>', extraclasses, s)
end
return p
5uerv9ulmkginz88extzxvojbs12dqw
281332
281331
2014-04-29T15:51:09Z
en>Mr. Stradivarius
0
merge rellink into hatnote, use an options table to pass options to the hatnote function, and add a tracking category for errors
281332
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p._findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('_findNamespaceId', 1, link, 'string')
checkType('_findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p._formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p._formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('_formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p._makeWikitextError(msg)
-- Formats an error message to be returned to wikitext.
checkType('_makeWikitextError', 1, msg, 'string')
local errorCategory = 'Hatnote templates with errors'
local errorCategoryLink = errorCategory
and string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
errorCategory
)
or ''
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
errorCategoryLink
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p._makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p._findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p._makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'rellink'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
sxa5300fwm4h9idg6nb1mngjinakj4u
281333
281332
2014-04-29T16:03:53Z
en>Mr. Stradivarius
0
add a demo parameter to makeWikitextError
281333
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p._findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('_findNamespaceId', 1, link, 'string')
checkType('_findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p._formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p._formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('_formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p._makeWikitextError(msg, demo)
-- Formats an error message to be returned to wikitext. If demo is not nil
-- or false, no error category is added.
checkType('_makeWikitextError', 1, msg, 'string')
local errorCategory = 'Hatnote templates with errors'
local errorCategoryLink
if not demo then
errorCategoryLink = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
errorCategory
)
else
errorCategoryLink = ''
end
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
errorCategoryLink
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p._makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p._findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p._makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'rellink'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
3g2gok1fjj0h84fkix2v6kvh1x0ss8w
281334
281333
2014-04-29T16:07:03Z
en>Mr. Stradivarius
0
add type check for makeWikitextError
281334
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p._findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('_findNamespaceId', 1, link, 'string')
checkType('_findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p._formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p._formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('_formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p._makeWikitextError(msg, demo)
-- Formats an error message to be returned to wikitext. If demo is not nil
-- or false, no error category is added.
checkType('_makeWikitextError', 1, msg, 'string')
checkType('_makeWikitextError', 2, demo, 'boolean', true)
local errorCategory = 'Hatnote templates with errors'
local errorCategoryLink
if demo then
errorCategoryLink = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
errorCategory
)
else
errorCategoryLink = ''
end
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
errorCategoryLink
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p._makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p._findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p._makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'rellink'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
m5tmxzynbd4a5c33nfdy9vnmh3p96mp
281335
281334
2014-04-29T16:16:30Z
en>Mr. Stradivarius
0
make makeWikitextError use [[Module:Yesno]] to parse the demo parameter, and remove underscores from all the function names, as that's normally done for private functions, whereas these are public
281335
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p.formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p.formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, demo)
-- Formats an error message to be returned to wikitext. If demo is not nil
-- or false, no error category is added.
checkType('makeWikitextError', 1, msg, 'string')
yesno = require('Module:Yesno')
local errorCategory = 'Hatnote templates with errors'
local errorCategoryLink
if yesno(demo) then
errorCategoryLink = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
errorCategory
)
else
errorCategoryLink = ''
end
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
errorCategoryLink
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError('no link specified')
end
return p.formatLink(link, display)
end
function p.formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('formatLink', 1, link, 'string')
checkType('formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'rellink'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
anvq9x6m8zf3guus8m30s6fit74g6fs
281336
281335
2014-04-29T16:19:51Z
en>Mr. Stradivarius
0
move formatLink back to the underscore - that one is necessary
281336
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p.formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p.formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, demo)
-- Formats an error message to be returned to wikitext. If demo is not nil
-- or false, no error category is added.
checkType('makeWikitextError', 1, msg, 'string')
yesno = require('Module:Yesno')
local errorCategory = 'Hatnote templates with errors'
local errorCategoryLink
if yesno(demo) then
errorCategoryLink = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
errorCategory
)
else
errorCategoryLink = ''
end
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
errorCategoryLink
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'rellink'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
prf7hq8r3b42x65w2geai9v33jzgmv4
281337
281336
2014-04-29T16:34:48Z
en>Mr. Stradivarius
0
fix error category logic
281337
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p.formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p.formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, demo)
-- Formats an error message to be returned to wikitext. If demo is not nil
-- or false, no error category is added.
checkType('makeWikitextError', 1, msg, 'string')
yesno = require('Module:Yesno')
local errorCategory = 'Hatnote templates with errors'
local errorCategoryLink
if yesno(demo) then
errorCategoryLink = ''
else
errorCategoryLink = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
errorCategory
)
end
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
errorCategoryLink
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'rellink'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
qa1jcdhp6upozk1fx7af8287df64tyo
281338
281337
2014-05-02T16:15:08Z
en>Mr. Stradivarius
0
fix function name
281338
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, demo)
-- Formats an error message to be returned to wikitext. If demo is not nil
-- or false, no error category is added.
checkType('makeWikitextError', 1, msg, 'string')
yesno = require('Module:Yesno')
local errorCategory = 'Hatnote templates with errors'
local errorCategoryLink
if yesno(demo) then
errorCategoryLink = ''
else
errorCategoryLink = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
errorCategory
)
end
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
errorCategoryLink
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'rellink'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
qrshdssaxgpytz26qp43ptgeleve2ww
281339
281338
2014-05-02T17:31:21Z
en>Mr. Stradivarius
0
use the blacklist from [[Module:Category handler]] in the makeWikitextError function
281339
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{rellink}} and {{hatnote}} meta-templates, and includes --
-- helper functions for other Lua modules to format hatnote links. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local mCategoryHandler -- lazily initialise [[Module:Category handler]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, demo)
-- Formats an error message to be returned to wikitext. If demo is not nil
-- or false, no error category is added.
checkType('makeWikitextError', 1, msg, 'string')
yesno = require('Module:Yesno')
mCategoryHandler = require('Module:Category handler')
local errorCategory = 'Hatnote templates with errors'
local errorCategoryLink = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
errorCategory
)
-- Feed the category link through [[Module:Category handler]] so we can
-- use its blacklist.
errorCategoryLink = mCategoryHandler.main{
all = errorCategoryLink,
nocat = demo
}
errorCategoryLink = errorCategoryLink or ''
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
errorCategoryLink
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#".
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'rellink'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
lo1qvv1wj0e8apqumr9d20fb70sezg1
281340
281339
2014-05-03T15:49:21Z
en>Mr. Stradivarius
0
change the rellink class to the new "hatnote" class, and update the comments
281340
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format hatnote link}} meta-templates, and --
-- includes helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local mCategoryHandler -- lazily initialise [[Module:Category handler]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, demo)
-- Formats an error message to be returned to wikitext. If demo is not nil
-- or false, no error category is added.
checkType('makeWikitextError', 1, msg, 'string')
yesno = require('Module:Yesno')
mCategoryHandler = require('Module:Category handler')
local errorCategory = 'Hatnote templates with errors'
local errorCategoryLink = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
errorCategory
)
-- Feed the category link through [[Module:Category handler]] so we can
-- use its blacklist.
errorCategoryLink = mCategoryHandler.main{
all = errorCategoryLink,
nocat = demo
}
errorCategoryLink = errorCategoryLink or ''
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
errorCategoryLink
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
c7tlu5rxzc83ygksn4bsuwrh0phti9u
281341
281340
2014-05-03T16:13:24Z
en>Mr. Stradivarius
0
don't categorise talk namespaces
281341
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format hatnote link}} meta-templates, and --
-- includes helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local mCategoryHandler -- lazily initialise [[Module:Category handler]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, demo)
-- Formats an error message to be returned to wikitext. If demo is not nil
-- or false, no error category is added.
checkType('makeWikitextError', 1, msg, 'string')
yesno = require('Module:Yesno')
mCategoryHandler = require('Module:Category handler')
local errorCategory = 'Hatnote templates with errors'
local errorCategoryLink = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
errorCategory
)
-- Feed the category link through [[Module:Category handler]] so we can
-- use its blacklist.
errorCategoryLink = mCategoryHandler.main{
talk = '', -- Don't categorise talk namespaces.
other = errorCategoryLink,
nocat = demo
}
errorCategoryLink = errorCategoryLink or ''
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
errorCategoryLink
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
ogijy6qsl8556aeqit89u9ptuse4lxj
281342
281341
2014-05-04T14:10:13Z
en>Mr. Stradivarius
0
update the makeWikitextError function - don't use [[Module:Category handler]], and use an addTrackingCategory parameter instead of a demo parameter, to make it easy for daughter modules to add a category=no parameter
281342
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format hatnote link}} meta-templates, and --
-- includes helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s.</strong>%s',
msg,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
apjmbcr4kpuzocodt2utr3p0irha8nf
281343
281342
2014-05-05T03:51:18Z
en>Mr. Stradivarius
0
add helpLink parameter to makeWikitextError
281343
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format hatnote link}} meta-templates, and --
-- includes helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError('no link specified')
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError('no text specified')
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
7dn7mgbzxcstwugcsowc8yokwe2001p
281344
281343
2014-05-05T04:10:29Z
en>Mr. Stradivarius
0
add a help link for the hatnote and formatLink functions
281344
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format hatnote link}} meta-templates, and --
-- includes helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
rs2kugjq5b28kp7xtxw3izt2gqzjqc8
281345
281344
2014-05-05T04:49:39Z
en>Mr. Stradivarius
0
Protected Module:Hatnote: [[WP:High-risk templates|High-risk Lua module]] ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite))
281344
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format hatnote link}} meta-templates, and --
-- includes helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
rs2kugjq5b28kp7xtxw3izt2gqzjqc8
281346
281345
2014-05-23T15:52:14Z
en>SMcCandlish
0
Adding ability to handle "inline" parameter; sandboxed this at [[Module:Hatnote/sandbox-inline]] and with [[Template:Hatnote-inline]]. I have big plans for this.
281346
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format hatnote link}} meta-templates, and --
-- includes helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
options.inline = args.inline
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
local inline = options.inline
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
if inline then
return string.format(
'<span class="%s">%s</span>',
table.concat(classes, ' '),
s
)
else
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
end
return p
kqzh7k1acoo4fcfno4qst3exvkizb6e
281347
281346
2014-05-23T23:28:43Z
en>Mr. Stradivarius
0
Undid revision 609825678 by [[Special:Contributions/SMcCandlish|SMcCandlish]] ([[User talk:SMcCandlish|talk]]) this change will have quite far-reaching consequences, so should be discussed first
281347
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format hatnote link}} meta-templates, and --
-- includes helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
rs2kugjq5b28kp7xtxw3izt2gqzjqc8
281348
281347
2014-10-07T10:35:12Z
en>Mr. Stradivarius
0
detect piped link hacks made with the {{!}} parser function
281348
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format hatnote link}} meta-templates, and --
-- includes helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
f8qq7zapswu550pbd1eicylvqkkrh8x
281349
281348
2015-01-16T18:15:24Z
en>Codename Lisa
0
Minor comment change
281349
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon
if namespace == 6 or namespace == 14 then
colon = ':'
else
colon = ''
end
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
fs8qikvs0az3qa5dvtw44fwva124xuz
281350
281349
2016-01-28T22:02:33Z
en>Ahecht
0
prefix all links with colons to allow interwiki links to work.
281350
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon = ':'
-- The following lines were commented out to allow interwiki links to work,
-- as there is no harm in prefixing all links with colons.
-- if namespace == 6 or namespace == 14 then
-- colon = ':'
-- else
-- colon = ''
-- end
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
9blpt0y42pp9yzuai3otakods6tvftf
281351
281350
2016-01-28T23:27:19Z
en>Mr. Stradivarius
0
tidy up the code now that we are adding colons to all the links
281351
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
-- Remove the initial colon for links where it was specified manually.
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[:%s|%s]]', link, display)
else
return string.format('[[:%s]]', link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
1d2pn0ccuejj0rf77o9asc6x25rxh0u
281352
281351
2016-01-28T23:31:35Z
en>Qed237
0
Reverted 1 edit by [[Special:Contributions/Mr. Stradivarius|Mr. Stradivarius]] ([[User talk:Mr. Stradivarius|talk]]): Error in module:main while using module:sports table. ([[WP:TW|TW]])
281352
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
-- Find whether we need to use the colon trick or not. We need to use the
-- colon trick for categories and files, as otherwise category links
-- categorise the page and file links display the file.
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
link = removeInitialColon(link)
local namespace = p.findNamespaceId(link, false)
local colon = ':'
-- The following lines were commented out to allow interwiki links to work,
-- as there is no harm in prefixing all links with colons.
-- if namespace == 6 or namespace == 14 then
-- colon = ':'
-- else
-- colon = ''
-- end
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[%s%s|%s]]', colon, link, display)
else
return string.format('[[%s%s]]', colon, link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
9blpt0y42pp9yzuai3otakods6tvftf
281353
281352
2016-01-28T23:35:36Z
en>Mr. Stradivarius
0
sorry about that; we need to keep findNamespaceId around for other modules
281353
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to true.
--
-- This function is used by other modules, e.g. [[Module:Main]].
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
local title = mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
-- Remove the initial colon for links where it was specified manually.
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[:%s|%s]]', link, display)
else
return string.format('[[:%s]]', link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
m2nl13ump4hev51soygck1w7mjfk36c
281354
281353
2016-01-29T05:40:14Z
en>Mr. Stradivarius
0
fix a mistake in the module comments; allow overriding the title in makeWikitextError; allow people to call _hatnote without an options table; and remove a comment about usage, as it is now covered in the test cases
281354
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
-- Remove the initial colon for links where it was specified manually.
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[:%s|%s]]', link, display)
else
return string.format('[[:%s]]', link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
69f0l5h73to3gr9ohpiisw42vokya9r
281355
281354
2016-02-12T01:36:19Z
en>Mr. Stradivarius
0
add role="note" for screen readers per protected edit request by [[User:Matt Fitzpatrick]]
281355
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
-- Remove the initial colon for links where it was specified manually.
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[:%s|%s]]', link, display)
else
return string.format('[[:%s]]', link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
h060jqjpajbuqyas63zq14fnqki7c1k
281356
281355
2016-05-05T17:11:59Z
en>Nihiltres
0
Added a simple p.disambiguate(page [, disambiguator]) helper function
281356
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
-- Remove the initial colon for links where it was specified manually.
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format('[[:%s|%s]]', link, display)
else
return string.format('[[:%s]]', link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
90w5195dak5qgbipdvvxgv48dsyovsd
281357
281356
2016-05-12T15:30:44Z
en>Nihiltres
0
Updated from sandbox: tweaked _formatLink() to overwrite, rather than stack, manual piping with "display"-based piping
281357
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
-- Remove the initial colon for links where it was specified manually.
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format(
'[[:%s|%s]]',
string.gsub(link, '|(.*)$', ''), --display overwrites manual piping
display
)
else
return string.format('[[:%s]]', link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
e40k01bl5t8mkn7r7tyh6kjxie5wfj6
281358
281357
2016-06-23T14:47:33Z
en>Nihiltres
0
Non-breaking space after "§" in _formatLink() to match [[Module:Section link]]; might not be a bad idea to require() that module but this is a good start
281358
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
-- Remove the initial colon for links where it was specified manually.
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format(
'[[:%s|%s]]',
string.gsub(link, '|(.*)$', ''), --display overwrites manual piping
display
)
else
return string.format('[[:%s]]', link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
n5em2uxcxeiqmcclh4ztmpn8lxpmavj
281359
281358
2017-05-09T05:20:32Z
en>JJMC89
0
Add navigation-not-searchable class per [[Special:Permalink/779475248#Add navigation-not-searchable class|request]] (also see [[phab:T164781|T164781]])
281359
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
-- Remove the initial colon for links where it was specified manually.
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format(
'[[:%s|%s]]',
string.gsub(link, '|(.*)$', ''), --display overwrites manual piping
display
)
else
return string.format('[[:%s]]', link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote', 'navigation-not-searchable'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
c5z31x97gktc3q7nnp8mf48t01l4qst
281360
281359
2020-06-16T14:56:40Z
en>Mr. Stradivarius
0
add "italicizepage" and "italicizesection" options to formatLink, per protected edit request by [[User:Psiĥedelisto]], with edits by myself
281360
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{link = page}
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink{link = link, display = display}
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the section, if it exists.
local page, section = link:match('^(.-)#(.*)$')
page = page or link
return {
link = link,
page = page,
section = section,
display = display,
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote', 'navigation-not-searchable'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
0s18z2k80d58lel6qlv1mmeuoa5nezz
281361
281360
2020-06-16T14:56:52Z
en>Mr. Stradivarius
0
Reverted edits by [[Special:Contribs/Mr. Stradivarius|Mr. Stradivarius]] ([[User talk:Mr. Stradivarius|talk]]) to last version by JJMC89
281361
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink(page)
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink(link, display)
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
local args = getArgs(frame)
local link = args[1]
local display = args[2]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink(link, display)
end
function p._formatLink(link, display)
checkType('_formatLink', 1, link, 'string')
checkType('_formatLink', 2, display, 'string', true)
-- Remove the initial colon for links where it was specified manually.
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
if not display then
local prePipe, postPipe = link:match('^(.-)|(.*)$')
link = prePipe or link
display = postPipe
end
-- Find the display value.
if not display then
local page, section = link:match('^(.-)#(.*)$')
if page then
display = page .. ' § ' .. section
end
end
-- Assemble the link.
if display then
return string.format(
'[[:%s|%s]]',
string.gsub(link, '|(.*)$', ''), --display overwrites manual piping
display
)
else
return string.format('[[:%s]]', link)
end
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote', 'navigation-not-searchable'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
c5z31x97gktc3q7nnp8mf48t01l4qst
281362
281361
2020-06-16T14:58:17Z
en>Mr. Stradivarius
0
reinstate the "italicizepage" and "italicizesection" arguments to _formatLink (I pulled the trigger a little bit early)
281362
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{link = page}
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink{link = link, display = display}
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage and yesno(addTrackingCategory) ~= false then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the section, if it exists.
local page, section = link:match('^(.-)#(.*)$')
page = page or link
return {
link = link,
page = page,
section = section,
display = display,
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote', 'navigation-not-searchable'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
0s18z2k80d58lel6qlv1mmeuoa5nezz
281363
281362
2020-07-14T15:05:40Z
en>Mr. Stradivarius
0
don't add pages in the user namespace to [[:Category:Hatnote templates with errors]]
281363
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{link = page}
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink{link = link, display = display}
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format hatnote link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format hatnote link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the section, if it exists.
local page, section = link:match('^(.-)#(.*)$')
page = page or link
return {
link = link,
page = page,
section = section,
display = display,
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote', 'navigation-not-searchable'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
4hr4hq4jl0a6zn957qst5w7e597j6v0
281364
281363
2020-07-29T00:47:18Z
en>Pppery
0
Remove stray space per edit request; update comment and error message for move of [[Template:Format hatnote link]] to [[Template:Format link]] in 2014
281364
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{link = page}
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink{link = link, display = display}
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the section, if it exists.
local page, section = link:match('^(.-)#(.*)$')
page = page or link
return {
link = link,
page = page,
section = section,
display = display,
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page or page == '' then
display = string.format('§ %s', section)
elseif section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote', 'navigation-not-searchable'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
c8kn8m8vep50dqkuhmpzcu3w4m1y036
281365
281364
2020-07-30T13:46:32Z
en>Mr. Stradivarius
0
parse pages for section-only links as nil - this prevents section-only links that have an italicized page parameter from having empty <i></i> tags
281365
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{link = page}
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink{link = link, display = display}
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = string.format('§ %s', section)
elseif section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote', 'navigation-not-searchable'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'<div role="note" class="%s">%s</div>',
table.concat(classes, ' '),
s
)
end
return p
i11f8q2u8pomoj3ukbihsqdql9a3juh
281366
281365
2021-07-12T04:21:02Z
en>Izno
0
we templatestyles now
281366
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{link = page}
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink{link = link, display = display}
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = string.format('§ %s', section)
elseif section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote', 'navigation-not-searchable'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'%s<div role="note" class="%s">%s</div>',
mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
},
table.concat(classes, ' '),
s
)
end
return p
8jl4tbplx9vbofd81vddfc42fqmupj3
281367
281366
2021-12-26T18:06:12Z
en>Nihiltres
0
Updated from sandbox, in particular to use [[Module:Format link]] and remove extracted functionality, and use mw.html and an inline option (credit Izno)
281367
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
p.missingTargetCat =
--Default missing target category, exported for use in related modules
'Articles with hatnote templates targeting a nonexistent page'
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(inline == 1 and 'hatnote-inline' or 'hatnote')
:addClass('navigation-not-searchable')
:addClass(extraclasses)
:addClass(options.selfref and 'selfref')
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
hac1b60ddsfqqpzx45ha2h9vvv343va
281368
281367
2021-12-26T19:13:26Z
en>Matthiaspaul
0
Undid revision 1062158757 by [[Special:Contributions/Nihiltres|Nihiltres]] ([[User talk:Nihiltres|talk]]) Temp undo as [[Template:See also]] now throws errors like "Lua error: bad argument #1 to 'title.new' (number or string expected, got nil)." (for example in article [[Bronshtein and Semendyayev]]).
281368
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.formatPages(...)
-- Formats a list of pages using formatLink and returns it as an array. Nil
-- values are not allowed.
local pages = {...}
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{link = page}
end
return ret
end
function p.formatPageTables(...)
-- Takes a list of page/display tables and returns it as a list of
-- formatted links. Nil values are not allowed.
local pages = {...}
local links = {}
for i, t in ipairs(pages) do
checkType('formatPageTables', i, t, 'table')
local link = t[1]
local display = t[2]
links[i] = p._formatLink{link = link, display = display}
end
return links
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = string.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return string.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return string.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = string.format('§ %s', section)
elseif section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
local options = {}
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
options.extraclasses = args.extraclasses
options.selfref = args.selfref
return p._hatnote(s, options)
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local classes = {'hatnote', 'navigation-not-searchable'}
local extraclasses = options.extraclasses
local selfref = options.selfref
if type(extraclasses) == 'string' then
classes[#classes + 1] = extraclasses
end
if selfref then
classes[#classes + 1] = 'selfref'
end
return string.format(
'%s<div role="note" class="%s">%s</div>',
mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
},
table.concat(classes, ' '),
s
)
end
return p
8jl4tbplx9vbofd81vddfc42fqmupj3
281369
281368
2021-12-26T20:05:22Z
en>Nihiltres
0
Undid revision 1062166786 by [[Special:Contributions/Matthiaspaul|Matthiaspaul]] ([[User talk:Matthiaspaul|talk]]); should be fixed now, and if not, please ping me with examples as I couldn't reproduce the original error
281369
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
p.missingTargetCat =
--Default missing target category, exported for use in related modules
'Articles with hatnote templates targeting a nonexistent page'
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(inline == 1 and 'hatnote-inline' or 'hatnote')
:addClass('navigation-not-searchable')
:addClass(extraclasses)
:addClass(options.selfref and 'selfref')
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
hac1b60ddsfqqpzx45ha2h9vvv343va
281370
281369
2022-01-04T17:41:17Z
en>Nihiltres
0
Updated from sandbox with namespace filtering for maintenance category
281370
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(inline == 1 and 'hatnote-inline' or 'hatnote')
:addClass('navigation-not-searchable')
:addClass(extraclasses)
:addClass(options.selfref and 'selfref')
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
81imvwvei9p1icm5aqz0d3b18lurz1c
281371
281370
2022-06-06T04:14:36Z
en>Nihiltres
0
Fix for fragile conditional
281371
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(inline == 1 and 'hatnote-inline' or 'hatnote')
:addClass('navigation-not-searchable')
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
3lp0p77brxpfdg1fgn1qvov8zvyjqlw
281372
281371
2022-08-18T19:31:48Z
en>Nihiltres
0
Updated from sandbox: export default classes through new function p.defaultClasses
281372
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
function p.defaultClasses(inline)
-- Provides the default hatnote classes as a space-separated string; useful
-- for hatnote-manipulation modules like [[Module:Hatnote group]].
return
(inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' ..
'navigation-not-searchable'
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(p.defaultClasses(inline))
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
ea8uo247e9xlc9dx1ceo587yefzyw7i
281373
281372
2022-09-05T18:18:32Z
en>Nihiltres
0
Reordered helper functions (first by export status, then alphabetically) and migrated p.quote upstream from [[Module:Redirect hatnote]] (includes contributions by Tamzin and Nihiltres)
281373
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.defaultClasses(inline)
-- Provides the default hatnote classes as a space-separated string; useful
-- for hatnote-manipulation modules like [[Module:Hatnote group]].
return
(inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' ..
'navigation-not-searchable'
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
function p.quote(title)
--Wraps titles in quotation marks. If the title starts/ends with a quotation
--mark, kerns that side as with {{-'}}
local quotationMarks = {
["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true
}
local quoteLeft, quoteRight = -- Test if start/end are quotation marks
quotationMarks[string.sub(title, 1, 1)],
quotationMarks[string.sub(title, -1, -1)]
if quoteLeft or quoteRight then
title = mw.html.create("span"):wikitext(title)
end
if quoteLeft then title:css("padding-left", "0.15em") end
if quoteRight then title:css("padding-right", "0.15em") end
return '"' .. tostring(title) .. '"'
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(p.defaultClasses(inline))
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
6vm2bp6g2nr605rzk5jap6wygn2ft2p
281374
281373
2025-03-18T15:42:51Z
en>Ahecht
0
Make empty function for direct invokation
281374
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.defaultClasses(inline)
-- Provides the default hatnote classes as a space-separated string; useful
-- for hatnote-manipulation modules like [[Module:Hatnote group]].
return
(inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' ..
'navigation-not-searchable'
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
function p.quote(title)
--Wraps titles in quotation marks. If the title starts/ends with a quotation
--mark, kerns that side as with {{-'}}
local quotationMarks = {
["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true
}
local quoteLeft, quoteRight = -- Test if start/end are quotation marks
quotationMarks[string.sub(title, 1, 1)],
quotationMarks[string.sub(title, -1, -1)]
if quoteLeft or quoteRight then
title = mw.html.create("span"):wikitext(title)
end
if quoteLeft then title:css("padding-left", "0.15em") end
if quoteRight then title:css("padding-right", "0.15em") end
return '"' .. tostring(title) .. '"'
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
p[''] = p.hatnote(frame:newChild{ title = "Template:Hatnote" })
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified: ' .. args[1],
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(p.defaultClasses(inline))
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
et13vio9xyl4840t826z870z66jnaoh
281375
281374
2025-03-18T15:44:20Z
en>Ahecht
0
debug
281375
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.defaultClasses(inline)
-- Provides the default hatnote classes as a space-separated string; useful
-- for hatnote-manipulation modules like [[Module:Hatnote group]].
return
(inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' ..
'navigation-not-searchable'
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
function p.quote(title)
--Wraps titles in quotation marks. If the title starts/ends with a quotation
--mark, kerns that side as with {{-'}}
local quotationMarks = {
["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true
}
local quoteLeft, quoteRight = -- Test if start/end are quotation marks
quotationMarks[string.sub(title, 1, 1)],
quotationMarks[string.sub(title, -1, -1)]
if quoteLeft or quoteRight then
title = mw.html.create("span"):wikitext(title)
end
if quoteLeft then title:css("padding-left", "0.15em") end
if quoteRight then title:css("padding-right", "0.15em") end
return '"' .. tostring(title) .. '"'
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
p[''] = function (frame) return p.hatnote(frame:newChild{ title = "Template:Hatnote" }) end
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified: ' .. args[1],
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(p.defaultClasses(inline))
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
qg0caondgj1dofs5e9fgvcqec14tuqi
281376
281375
2025-03-18T15:44:58Z
en>Canterbury Tail
0
rv this is throwing errors all over the project.
281376
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.defaultClasses(inline)
-- Provides the default hatnote classes as a space-separated string; useful
-- for hatnote-manipulation modules like [[Module:Hatnote group]].
return
(inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' ..
'navigation-not-searchable'
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
function p.quote(title)
--Wraps titles in quotation marks. If the title starts/ends with a quotation
--mark, kerns that side as with {{-'}}
local quotationMarks = {
["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true
}
local quoteLeft, quoteRight = -- Test if start/end are quotation marks
quotationMarks[string.sub(title, 1, 1)],
quotationMarks[string.sub(title, -1, -1)]
if quoteLeft or quoteRight then
title = mw.html.create("span"):wikitext(title)
end
if quoteLeft then title:css("padding-left", "0.15em") end
if quoteRight then title:css("padding-right", "0.15em") end
return '"' .. tostring(title) .. '"'
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(p.defaultClasses(inline))
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
6vm2bp6g2nr605rzk5jap6wygn2ft2p
281377
281376
2025-03-18T15:46:22Z
en>Ahecht
0
That should've gone in the sandbox
281377
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.defaultClasses(inline)
-- Provides the default hatnote classes as a space-separated string; useful
-- for hatnote-manipulation modules like [[Module:Hatnote group]].
return
(inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' ..
'navigation-not-searchable'
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
function p.quote(title)
--Wraps titles in quotation marks. If the title starts/ends with a quotation
--mark, kerns that side as with {{-'}}
local quotationMarks = {
["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true
}
local quoteLeft, quoteRight = -- Test if start/end are quotation marks
quotationMarks[string.sub(title, 1, 1)],
quotationMarks[string.sub(title, -1, -1)]
if quoteLeft or quoteRight then
title = mw.html.create("span"):wikitext(title)
end
if quoteLeft then title:css("padding-left", "0.15em") end
if quoteRight then title:css("padding-right", "0.15em") end
return '"' .. tostring(title) .. '"'
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
p[''] = function (frame) return p.hatnote(frame:newChild{ title = "Template:Hatnote" }) end
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified: ',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(p.defaultClasses(inline))
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
ba4hiwy0t89zsi7g0dtgt89e2pxc4w5
281378
281377
2025-03-18T15:47:33Z
en>Ahecht
0
Revert
281378
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.defaultClasses(inline)
-- Provides the default hatnote classes as a space-separated string; useful
-- for hatnote-manipulation modules like [[Module:Hatnote group]].
return
(inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' ..
'navigation-not-searchable'
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
function p.quote(title)
--Wraps titles in quotation marks. If the title starts/ends with a quotation
--mark, kerns that side as with {{-'}}
local quotationMarks = {
["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true
}
local quoteLeft, quoteRight = -- Test if start/end are quotation marks
quotationMarks[string.sub(title, 1, 1)],
quotationMarks[string.sub(title, -1, -1)]
if quoteLeft or quoteRight then
title = mw.html.create("span"):wikitext(title)
end
if quoteLeft then title:css("padding-left", "0.15em") end
if quoteRight then title:css("padding-right", "0.15em") end
return '"' .. tostring(title) .. '"'
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
p[''] = function (frame) return p.hatnote(frame:newChild{ title = "Template:Hatnote" }) end
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(p.defaultClasses(inline))
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
qps4k9ys6ba1dq9msz6m6lq3kfc4w6t
281379
281378
2026-06-07T17:48:01Z
Ameisenigel
44
65 revisions imported from [[:en:Module:Hatnote]]: Request at [[WF:AN]]
281378
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote --
-- --
-- This module produces hatnote links and links to related articles. It --
-- implements the {{hatnote}} and {{format link}} meta-templates and includes --
-- helper functions for other Lua hatnote modules. --
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local formatLink -- lazily initialise [[Module:Format link]] ._formatLink
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
function p.defaultClasses(inline)
-- Provides the default hatnote classes as a space-separated string; useful
-- for hatnote-manipulation modules like [[Module:Hatnote group]].
return
(inline == 1 and 'hatnote-inline' or 'hatnote') .. ' ' ..
'navigation-not-searchable'
end
function p.disambiguate(page, disambiguator)
-- Formats a page title with a disambiguation parenthetical,
-- i.e. "Example" → "Example (disambiguation)".
checkType('disambiguate', 1, page, 'string')
checkType('disambiguate', 2, disambiguator, 'string', true)
disambiguator = disambiguator or 'disambiguation'
return mw.ustring.format('%s (%s)', page, disambiguator)
end
function p.findNamespaceId(link, removeColon)
-- Finds the namespace id (namespace number) of a link or a pagename. This
-- function will not work if the link is enclosed in double brackets. Colons
-- are trimmed from the start of the link by default. To skip colon
-- trimming, set the removeColon parameter to false.
checkType('findNamespaceId', 1, link, 'string')
checkType('findNamespaceId', 2, removeColon, 'boolean', true)
if removeColon ~= false then
link = removeInitialColon(link)
end
local namespace = link:match('^(.-):')
if namespace then
local nsTable = mw.site.namespaces[namespace]
if nsTable then
return nsTable.id
end
end
return 0
end
function p.makeWikitextError(msg, helpLink, addTrackingCategory, title)
-- Formats an error message to be returned to wikitext. If
-- addTrackingCategory is not false after being returned from
-- [[Module:Yesno]], and if we are not on a talk page, a tracking category
-- is added.
checkType('makeWikitextError', 1, msg, 'string')
checkType('makeWikitextError', 2, helpLink, 'string', true)
yesno = require('Module:Yesno')
title = title or mw.title.getCurrentTitle()
-- Make the help link text.
local helpText
if helpLink then
helpText = ' ([[' .. helpLink .. '|help]])'
else
helpText = ''
end
-- Make the category text.
local category
if not title.isTalkPage -- Don't categorise talk pages
and title.namespace ~= 2 -- Don't categorise userspace
and yesno(addTrackingCategory) ~= false -- Allow opting out
then
category = 'Hatnote templates with errors'
category = mw.ustring.format(
'[[%s:%s]]',
mw.site.namespaces[14].name,
category
)
else
category = ''
end
return mw.ustring.format(
'<strong class="error">Error: %s%s.</strong>%s',
msg,
helpText,
category
)
end
local curNs = mw.title.getCurrentTitle().namespace
p.missingTargetCat =
--Default missing target category, exported for use in related modules
((curNs == 0) or (curNs == 14)) and
'Articles with hatnote templates targeting a nonexistent page' or nil
function p.quote(title)
--Wraps titles in quotation marks. If the title starts/ends with a quotation
--mark, kerns that side as with {{-'}}
local quotationMarks = {
["'"]=true, ['"']=true, ['“']=true, ["‘"]=true, ['”']=true, ["’"]=true
}
local quoteLeft, quoteRight = -- Test if start/end are quotation marks
quotationMarks[string.sub(title, 1, 1)],
quotationMarks[string.sub(title, -1, -1)]
if quoteLeft or quoteRight then
title = mw.html.create("span"):wikitext(title)
end
if quoteLeft then title:css("padding-left", "0.15em") end
if quoteRight then title:css("padding-right", "0.15em") end
return '"' .. tostring(title) .. '"'
end
--------------------------------------------------------------------------------
-- Hatnote
--
-- Produces standard hatnote text. Implements the {{hatnote}} template.
--------------------------------------------------------------------------------
p[''] = function (frame) return p.hatnote(frame:newChild{ title = "Template:Hatnote" }) end
function p.hatnote(frame)
local args = getArgs(frame)
local s = args[1]
if not s then
return p.makeWikitextError(
'no text specified',
'Template:Hatnote#Errors',
args.category
)
end
return p._hatnote(s, {
extraclasses = args.extraclasses,
selfref = args.selfref
})
end
function p._hatnote(s, options)
checkType('_hatnote', 1, s, 'string')
checkType('_hatnote', 2, options, 'table', true)
options = options or {}
local inline = options.inline
local hatnote = mw.html.create(inline == 1 and 'span' or 'div')
local extraclasses
if type(options.extraclasses) == 'string' then
extraclasses = options.extraclasses
end
hatnote
:attr('role', 'note')
:addClass(p.defaultClasses(inline))
:addClass(extraclasses)
:addClass(options.selfref and 'selfref' or nil)
:wikitext(s)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles', args = { src = 'Module:Hatnote/styles.css' }
} .. tostring(hatnote)
end
return p
qps4k9ys6ba1dq9msz6m6lq3kfc4w6t
Module:Hatnote/styles.css
828
85257
281380
2019-07-20T21:48:05Z
en>Galobtter
0
create
281380
sanitized-css
text/css
/* {{pp-template}} */
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
div.hatnote + div.hatnote {
margin-top: -0.5em;
}
mkub11nuwn0yrlz7mk1rgmwdt5hbll0
281381
281380
2019-07-20T21:50:24Z
en>MusikBot
0
Removing protection templates from unprotected page ([[User:MusikBot/FixPP/FAQ|more info]])
281381
sanitized-css
text/css
/* */
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
div.hatnote + div.hatnote {
margin-top: -0.5em;
}
843cl5p3j64kszdqono3dk0pufxvzz8
281382
281381
2019-07-20T22:45:29Z
en>Galobtter
0
-
281382
sanitized-css
text/css
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
div.hatnote + div.hatnote {
margin-top: -0.5em;
}
ftg6fuvfjhexjbqfy6if02uvd4v53f8
281383
281382
2019-07-20T22:50:40Z
en>Galobtter
0
fix
281383
sanitized-css
text/css
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
div.hatnote + div.hatnote, div.hatnote + link + div.hatnote {
margin-top: -0.5em;
}
1ijzhehheladvt6kuvju4z2aaorqal1
281384
281383
2019-07-20T22:53:45Z
en>Galobtter
0
+
281384
sanitized-css
text/css
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
/*
* The templatestyles element inserts a link element before every hatnote.
* Technically div.hatnote + div.hatnote is not needed. Remove?
*/
div.hatnote + div.hatnote, div.hatnote + link + div.hatnote {
margin-top: -0.5em;
}
87jmmz5vw21lvd53tdxse054n154lq5
281385
281384
2019-07-20T22:55:50Z
en>Galobtter
0
fix
281385
sanitized-css
text/css
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
/*
* The templatestyles element inserts a link element before every hatnote.
* Technically div.hatnote + div.hatnote is not needed, only div.hatnote + link + div.hatnote. Remove?
*/
div.hatnote + div.hatnote, div.hatnote + link + div.hatnote {
margin-top: -0.5em;
}
4v8igj4yxi4buqlpes263n7tmu95ydq
281386
281385
2019-07-20T23:41:00Z
en>Galobtter
0
+
281386
sanitized-css
text/css
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
/* The templatestyles element inserts a link element before every hatnote. */
div.hatnote + link + div.hatnote {
margin-top: -0.5em;
}
bud6ier5e5q0quwcu5xdegfpsnzxusa
281387
281386
2019-07-20T23:41:16Z
en>Galobtter
0
fmt
281387
sanitized-css
text/css
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
/* The templatestyles element inserts a link element before every hatnote. */
div.hatnote + link + div.hatnote {
margin-top: -0.5em;
}
3zvnub6lda53kudxn89ffuxmzmnun8h
281388
281387
2019-07-20T23:44:22Z
en>Galobtter
0
fix
281388
sanitized-css
text/css
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
/* The templatestyles element inserts a link element before hatnotes. */
div.hatnote + link + div.hatnote {
margin-top: -0.5em;
}
s2pibr4gsulv82l0x1mv2guufqalyn6
281389
281388
2019-07-26T04:50:18Z
en>Izno
0
per guidelines
281389
sanitized-css
text/css
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
/* The templatestyles element inserts a link element before hatnotes. */
div.hatnote + link + div.hatnote {
margin-top: -0.5em;
}
ke6rksjy3aaubn4vzbdj3xglxtfb9sj
281390
281389
2021-04-29T19:49:45Z
en>Izno
0
add a note
281390
sanitized-css
text/css
.hatnote {
font-style: italic;
}
.hatnote i {
font-style: normal;
}
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
/* The templatestyles element inserts a link element before hatnotes.
* TODO: Remove link if/when WMF resolves T200206 */
div.hatnote + link + div.hatnote {
margin-top: -0.5em;
}
f60hhgy2vjrxecov1spkhdirf6aesp2
281391
281390
2021-04-29T19:53:24Z
en>Izno
0
I didn't see anywhere why that needed to be a separate css block for blocks
281391
sanitized-css
text/css
.hatnote {
font-style: italic;
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles element inserts a link element before hatnotes.
* TODO: Remove link if/when WMF resolves T200206 */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
26imeske35ws5yguimy0qk7ixzv6sag
281392
281391
2021-07-12T04:17:38Z
en>Izno
0
add template
281392
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles element inserts a link element before hatnotes.
* TODO: Remove link if/when WMF resolves T200206 */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
5ehj1tt48lpfado44nkrmj003yx54ns
281393
281392
2021-07-12T04:18:07Z
en>Izno
0
Protected "[[Module:Hatnote/styles.css]]": [[WP:High-risk templates|High-risk Lua module]]: match parent ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
281392
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles element inserts a link element before hatnotes.
* TODO: Remove link if/when WMF resolves T200206 */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
5ehj1tt48lpfado44nkrmj003yx54ns
281394
281393
2021-07-12T19:22:27Z
en>Izno
0
per my talk page
281394
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles element inserts a link element before hatnotes.
* TODO: Remove link if/when WMF resolves T200206 */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
7znvdfdw9mn3ta8a2ioyp35gizwnv4c
281395
281394
2024-07-22T21:11:47Z
en>Izno
0
move @print none to templatestyles from [[MediaWiki:Print.css]] to support [[MediaWiki talk:Common.css/to do#Turn mobile.css/js totally off]]
281395
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles element inserts a link element before hatnotes.
* TODO: Remove link if/when WMF resolves T200206 */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
@media print {
body.ns-0 .hatnote {
display: none !important;
}
}
jwhkjblpyf93ejffkuu68hxj9zpt08y
281396
281395
2025-11-04T12:42:16Z
en>TheDJ
0
avoid background bleeding into float
281396
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
/* Separate block formatting context avoids background behind floating content */
display: flow-root;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles element inserts a link element before hatnotes.
* TODO: Remove link if/when WMF resolves T200206 */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
@media print {
body.ns-0 .hatnote {
display: none !important;
}
}
d0520g36gucvl6cm772zxqf2upqw8kq
281397
281396
2025-11-04T18:50:41Z
en>Izno
0
rv gf: we should not attempt to hack around upstream IMO, the correct fix is deciding [[Module_talk:Hatnote#Mobile_styling|whether we even want those colors]] and/or correcting that issue in WikimediaMessages
281397
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles element inserts a link element before hatnotes.
* TODO: Remove link if/when WMF resolves T200206 */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
@media print {
body.ns-0 .hatnote {
display: none !important;
}
}
jwhkjblpyf93ejffkuu68hxj9zpt08y
281398
281397
2026-05-11T21:35:05Z
en>Izno
0
[[phab:T378906]] / [[phab:T425364]]
281398
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles causes an 'empty' span between hatnotes */
.hatnote + span.mw-empty-elt + .hatnote {
margin-top: -0.5em;
}
@media print {
body.ns-0 .hatnote {
display: none !important;
}
}
6gskmzdtizand5u7pzecbsqp8m24jjd
281399
281398
2026-05-11T21:43:33Z
en>Izno
0
and readd
281399
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles causes an 'empty' span between hatnotes */
.hatnote + span.mw-empty-elt + .hatnote,
/* remove this version when Parsoid is the only parser */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
@media print {
body.ns-0 .hatnote {
display: none !important;
}
}
s3trgjofrxwudkwkkdld4cjd5maicyl
281400
281399
2026-05-11T21:45:33Z
en>Izno
0
and a link to T200206
281400
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles causes an 'empty' span between hatnotes */
.hatnote + span.mw-empty-elt + .hatnote,
/* remove this version when Parsoid is the only parser, see also [[phab:T200206]] */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
@media print {
body.ns-0 .hatnote {
display: none !important;
}
}
kodf3qg9mssfec1mtmjsyettudfade7
281401
281400
2026-05-11T21:45:51Z
en>Izno
0
selector
281401
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles causes an 'empty' span between hatnotes */
.hatnote + span.mw-empty-elt + .hatnote,
/* remove this selector when Parsoid is the only parser, see also [[phab:T200206]] */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
@media print {
body.ns-0 .hatnote {
display: none !important;
}
}
8oift3eve95te8chs4adr5fpl4pmpmd
281402
281401
2026-06-07T17:49:06Z
Ameisenigel
44
22 revisions imported from [[:en:Module:Hatnote/styles.css]]: Request at [[WF:AN]]
281401
sanitized-css
text/css
/* {{pp|small=y}} */
.hatnote {
font-style: italic;
}
/* Limit structure CSS to divs because of [[Module:Hatnote inline]] */
div.hatnote {
/* @noflip */
padding-left: 1.6em;
margin-bottom: 0.5em;
}
.hatnote i {
font-style: normal;
}
/* The templatestyles causes an 'empty' span between hatnotes */
.hatnote + span.mw-empty-elt + .hatnote,
/* remove this selector when Parsoid is the only parser, see also [[phab:T200206]] */
.hatnote + link + .hatnote {
margin-top: -0.5em;
}
@media print {
body.ns-0 .hatnote {
display: none !important;
}
}
8oift3eve95te8chs4adr5fpl4pmpmd
Module:Hatnote list
828
85258
281403
2016-04-08T17:00:00Z
en>Nihiltres
0
Not sure if it's appropriate for [[Module:Hatnote]] proper, but it can be its own module and we can just merge it in later, probably
281403
Scribunto
text/plain
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants. Also incidentally
-- introduces andList & orList helpers, useful for other hatnote lists.
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.andList (andTable)
-- Stringifies a list with "and"
local andString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', and ')) or
mw.text.listToText(andTable)
return andString
end
function p.orList (orTable)
-- Stringifies a list with "or"
local orString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', or ')) or
mw.text.listToText(andTable, nil, ' or ')
return orString
end
function p.forSee (frame, from, options)
-- Calls _forSee but pulls from the frame.
mArguments = require('Module:Arguments')
getArgs = mArguments.getArgs
local args = getArgs(frame)
return p._forSee(args, from, options)
end
function p._forSee (args, from, options)
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
-- Type-checks and defaults
checkType("_forSee", 1, args, 'table')
checkType("_forSee", 2, from, 'number', true)
from = from or 1
checkType("_forSee", 3, options, 'table', true)
options = options or {}
local defaultOptions = {
title = mw.title.getCurrentTitle().text,
disambiguator = ' (disambiguation)',
otherText = 'other uses'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Each row's a table with one "use" string and one "see" table
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.see = {}
-- If there's not at least one page listed, assume the list's ended, use
-- the default, and break at end of this loop-through.
table.insert(forRow.see, args[i + 1] or (options.title .. options.disambiguator))
if not args[i + 1] then terminated = true end
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.see, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
-- Stringify the table, which is easy because it's structured now
local strList = {}
for k, v in pairs(forTable) do
local useStr = v.use
local seeStr = p.andList(mHatnote.formatPages(unpack(v.see)))
table.insert(strList, string.format('For %s, see %s.', useStr, seeStr))
end
return mw.text.listToText(strList, ' ', ' ')
end
return p
kjgcgm3pf81z4bc7je86berfil72wsv
281404
281403
2016-04-18T21:33:05Z
en>Nihiltres
0
Removed behaviour of terminating list at empty page parameter; inconsistent with behaviour elsewhere
281404
Scribunto
text/plain
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants. Also incidentally
-- introduces andList & orList helpers, useful for other hatnote lists.
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.andList (andTable)
-- Stringifies a list with "and"
local andString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', and ')) or
mw.text.listToText(andTable)
return andString
end
function p.orList (orTable)
-- Stringifies a list with "or"
local orString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', or ')) or
mw.text.listToText(andTable, nil, ' or ')
return orString
end
function p.forSee (frame, from, options)
-- Calls _forSee but pulls from the frame.
mArguments = require('Module:Arguments')
getArgs = mArguments.getArgs
local args = getArgs(frame)
return p._forSee(args, from, options)
end
function p._forSee (args, from, options)
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
-- Type-checks and defaults
checkType("_forSee", 1, args, 'table')
checkType("_forSee", 2, from, 'number', true)
from = from or 1
checkType("_forSee", 3, options, 'table', true)
options = options or {}
local defaultOptions = {
title = mw.title.getCurrentTitle().text,
disambiguator = ' (disambiguation)',
otherText = 'other uses'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Each row's a table with one "use" string and one "see" table
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.see = {}
-- If there's not at least one page listed, assume the list's ended, use
-- the default, and break at end of this loop-through.
table.insert(forRow.see, args[i + 1] or (options.title .. options.disambiguator))
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.see, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
-- Stringify the table, which is easy because it's structured now
local strList = {}
for k, v in pairs(forTable) do
local useStr = v.use
local seeStr = p.andList(mHatnote.formatPages(unpack(v.see)))
table.insert(strList, string.format('For %s, see %s.', useStr, seeStr))
end
return mw.text.listToText(strList, ' ', ' ')
end
return p
f8dawvntcitabcj1ulflmpbzts485ic
281405
281404
2016-04-21T13:51:11Z
en>Nihiltres
0
Moved form of for-see statement into options
281405
Scribunto
text/plain
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants. Also incidentally
-- introduces andList & orList helpers, useful for other hatnote lists.
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.andList (andTable)
-- Stringifies a list with "and"
local andString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', and ')) or
mw.text.listToText(andTable)
return andString
end
function p.orList (orTable)
-- Stringifies a list with "or"
local orString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', or ')) or
mw.text.listToText(andTable, nil, ' or ')
return orString
end
function p.forSee (frame, from, options)
-- Calls _forSee but pulls from the frame.
mArguments = require('Module:Arguments')
getArgs = mArguments.getArgs
local args = getArgs(frame)
return p._forSee(args, from, options)
end
function p._forSee (args, from, options)
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
-- Type-checks and defaults
checkType("_forSee", 1, args, 'table')
checkType("_forSee", 2, from, 'number', true)
from = from or 1
checkType("_forSee", 3, options, 'table', true)
options = options or {}
local defaultOptions = {
disambiguator = ' (disambiguation)',
forseeForm = 'For %s, see %s.',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Each row's a table with one "use" string and one "see" table
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.see = {}
-- If there's not at least one page listed, assume the list's ended, use
-- the default, and break at end of this loop-through.
table.insert(forRow.see, args[i + 1] or (options.title .. options.disambiguator))
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.see, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
-- Stringify the table, which is easy because it's structured now
local strList = {}
for k, v in pairs(forTable) do
local useStr = v.use
local seeStr = p.andList(mHatnote.formatPages(unpack(v.see)))
table.insert(strList, string.format(options.forseeForm, useStr, seeStr))
end
return mw.text.listToText(strList, ' ', ' ')
end
return p
98p3m287om81f8zma0zfjnje9r2kf26
281406
281405
2016-04-27T15:29:04Z
en>Nihiltres
0
Changed some variable and structure names: forRow's second item is a table called "pages" instead of a table called "see" now; more logical naming
281406
Scribunto
text/plain
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants. Also incidentally
-- introduces andList & orList helpers, useful for other hatnote lists.
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.andList (andTable)
-- Stringifies a list with "and"
local andString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', and ')) or
mw.text.listToText(andTable)
return andString
end
function p.orList (orTable)
-- Stringifies a list with "or"
local orString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', or ')) or
mw.text.listToText(andTable, nil, ' or ')
return orString
end
function p.forSee (frame, from, options)
-- Calls _forSee but pulls from the frame.
mArguments = require('Module:Arguments')
getArgs = mArguments.getArgs
local args = getArgs(frame)
return p._forSee(args, from, options)
end
function p._forSee (args, from, options)
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
-- Type-checks and defaults
checkType("_forSee", 1, args, 'table')
checkType("_forSee", 2, from, 'number', true)
from = from or 1
checkType("_forSee", 3, options, 'table', true)
options = options or {}
local defaultOptions = {
disambiguator = ' (disambiguation)',
forseeForm = 'For %s, see %s.',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Each row's a table with one "use" string and one "see" table
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- If there's not at least one page listed, assume the list's ended, use
-- the default, and break at end of this loop-through.
table.insert(forRow.pages, args[i + 1] or (options.title .. options.disambiguator))
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
-- Stringify the table, which is easy because it's structured now
local strList = {}
for k, v in pairs(forTable) do
local useStr = v.use
local pagesStr = p.andList(mHatnote.formatPages(unpack(v.pages)))
table.insert(strList, string.format(options.forseeForm, useStr, pagesStr))
end
return mw.text.listToText(strList, ' ', ' ')
end
return p
gk2eahjd8r6yoalnodryrgwsxl8jk1u
281407
281406
2016-04-27T16:57:08Z
en>Nihiltres
0
Tweaked a comment to reflect last edit, and fixed an outdated comment
281407
Scribunto
text/plain
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants. Also incidentally
-- introduces andList & orList helpers, useful for other hatnote lists.
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.andList (andTable)
-- Stringifies a list with "and"
local andString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', and ')) or
mw.text.listToText(andTable)
return andString
end
function p.orList (orTable)
-- Stringifies a list with "or"
local orString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', or ')) or
mw.text.listToText(andTable, nil, ' or ')
return orString
end
function p.forSee (frame, from, options)
-- Calls _forSee but pulls from the frame.
mArguments = require('Module:Arguments')
getArgs = mArguments.getArgs
local args = getArgs(frame)
return p._forSee(args, from, options)
end
function p._forSee (args, from, options)
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
-- Type-checks and defaults
checkType("_forSee", 1, args, 'table')
checkType("_forSee", 2, from, 'number', true)
from = from or 1
checkType("_forSee", 3, options, 'table', true)
options = options or {}
local defaultOptions = {
disambiguator = ' (disambiguation)',
forseeForm = 'For %s, see %s.',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Rows are tables of a "use" string and a "pages" table of pagename strings
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- If there's not at least one page listed, use the default, and break
-- at the end of this loop-through.
table.insert(forRow.pages, args[i + 1] or (options.title .. options.disambiguator))
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
-- Stringify the table, which is easy because it's structured now
local strList = {}
for k, v in pairs(forTable) do
local useStr = v.use
local pagesStr = p.andList(mHatnote.formatPages(unpack(v.pages)))
table.insert(strList, string.format(options.forseeForm, useStr, pagesStr))
end
return mw.text.listToText(strList, ' ', ' ')
end
return p
tjilxbh2w6iucv2lv233pfemx58weha
281408
281407
2016-04-28T15:02:19Z
en>Nihiltres
0
Derped on that comment fix before. Removed more incorrect/obsolete comment text.
281408
Scribunto
text/plain
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants. Also incidentally
-- introduces andList & orList helpers, useful for other hatnote lists.
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.andList (andTable)
-- Stringifies a list with "and"
local andString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', and ')) or
mw.text.listToText(andTable)
return andString
end
function p.orList (orTable)
-- Stringifies a list with "or"
local orString = (#andTable > 2 and mw.text.listToText(andTable, nil, ', or ')) or
mw.text.listToText(andTable, nil, ' or ')
return orString
end
function p.forSee (frame, from, options)
-- Calls _forSee but pulls from the frame.
mArguments = require('Module:Arguments')
getArgs = mArguments.getArgs
local args = getArgs(frame)
return p._forSee(args, from, options)
end
function p._forSee (args, from, options)
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
-- Type-checks and defaults
checkType("_forSee", 1, args, 'table')
checkType("_forSee", 2, from, 'number', true)
from = from or 1
checkType("_forSee", 3, options, 'table', true)
options = options or {}
local defaultOptions = {
disambiguator = ' (disambiguation)',
forseeForm = 'For %s, see %s.',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Rows are tables of a "use" string and a "pages" table of pagename strings
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- If there's not at least one page listed, use the default.
table.insert(forRow.pages, args[i + 1] or (options.title .. options.disambiguator))
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
-- Stringify the table, which is easy because it's structured now
local strList = {}
for k, v in pairs(forTable) do
local useStr = v.use
local pagesStr = p.andList(mHatnote.formatPages(unpack(v.pages)))
table.insert(strList, string.format(options.forseeForm, useStr, pagesStr))
end
return mw.text.listToText(strList, ' ', ' ')
end
return p
2jrqmo2q25m9izo3lq33yx78to8jkmy
281409
281408
2016-04-29T03:47:19Z
en>Nihiltres
0
Simplified a few things, mostly re: calls to mw.listToText().
281409
Scribunto
text/plain
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants. Also incidentally
-- introduces andList & orList helpers, useful for other hatnote lists.
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.andList (list)
-- Stringifies a list with "and"
return mw.text.listToText(list, nil, (#list > 2 and ',' or '') .. ' and ')
end
function p.orList (list)
-- Stringifies a list with "or"
return mw.text.listToText(list, nil, (#list > 2 and ',' or '') .. ' or ')
end
function p.forSee (frame, from, options)
-- Calls _forSee but pulls from the frame.
mArguments = require('Module:Arguments')
getArgs = mArguments.getArgs
local args = getArgs(frame)
return p._forSee(args, from, options)
end
function p._forSee (args, from, options)
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
-- Type-checks and defaults
checkType("_forSee", 1, args, 'table')
checkType("_forSee", 2, from, 'number', true)
from = from or 1
checkType("_forSee", 3, options, 'table', true)
options = options or {}
local defaultOptions = {
disambiguator = ' (disambiguation)',
forseeForm = 'For %s, see %s.',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Rows are tables of a "use" string and a "pages" table of pagename strings
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- If there's not at least one page listed, use the default.
table.insert(forRow.pages, args[i + 1] or (options.title .. options.disambiguator))
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
-- Stringify the table, which is easy because it's structured now
local strList = {}
for k, v in pairs(forTable) do
local useStr = v.use
local pagesStr = p.andList(mHatnote.formatPages(unpack(v.pages)))
table.insert(strList, string.format(options.forseeForm, useStr, pagesStr))
end
return table.concat(strList, ' ')
end
return p
7mkrbdzjxvjau96a8kdh67fpl3uv1fo
281410
281409
2016-04-29T03:55:53Z
en>Nihiltres
0
Simplified p.forSee
281410
Scribunto
text/plain
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants. Also incidentally
-- introduces andList & orList helpers, useful for other hatnote lists.
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
function p.andList (list)
-- Stringifies a list with "and"
return mw.text.listToText(list, nil, (#list > 2 and ',' or '') .. ' and ')
end
function p.orList (list)
-- Stringifies a list with "or"
return mw.text.listToText(list, nil, (#list > 2 and ',' or '') .. ' or ')
end
function p.forSee (frame, from, options)
-- Calls _forSee but pulls from the frame.
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
function p._forSee (args, from, options)
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
-- Type-checks and defaults
checkType("_forSee", 1, args, 'table')
checkType("_forSee", 2, from, 'number', true)
from = from or 1
checkType("_forSee", 3, options, 'table', true)
options = options or {}
local defaultOptions = {
disambiguator = ' (disambiguation)',
forseeForm = 'For %s, see %s.',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses'
}
for k, v in pairs(defaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Rows are tables of a "use" string and a "pages" table of pagename strings
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- If there's not at least one page listed, use the default.
table.insert(forRow.pages, args[i + 1] or (options.title .. options.disambiguator))
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
-- Stringify the table, which is easy because it's structured now
local strList = {}
for k, v in pairs(forTable) do
local useStr = v.use
local pagesStr = p.andList(mHatnote.formatPages(unpack(v.pages)))
table.insert(strList, string.format(options.forseeForm, useStr, pagesStr))
end
return table.concat(strList, ' ')
end
return p
s6vq5k2vntdtmus86thg0mkbihbqs5h
281411
281410
2016-05-05T16:25:03Z
en>Nihiltres
0
Updated from sandbox
281411
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " "
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if string.find(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and string.find(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
-- Stringifies a list with "and"
function p.andList (list)
return stringifyList(list, {conjunction = "and"})
end
-- Stringifies a list with "or"
function p.orList (list)
return stringifyList(list, {conjunction = "or"})
end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
disambiguator = ' (disambiguation)',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forseeForm = 'For %s, see %s.',
punctuationCollapseReplacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
}
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Rows are tables of a "use" string and a "pages" table of pagename strings
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- If there's not at least one page listed, use the default.
table.insert(forRow.pages, args[i + 1] or (options.title .. options.disambiguator))
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Takes a table as formatted by forSeeArgsToTable and stringifies it
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table")
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Format each for-see item and make a table containing them
local strList = {}
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(mHatnote.formatPages(unpack(v.pages))) or
mHatnote._formatLink(options.title .. options.disambiguator)
local forSeeStr = string.format(options.forseeForm, useStr, pagesStr)
for k, v in pairs(options.punctuationCollapseReplacements) do
forSeeStr = string.gsub(forSeeStr, k, v)
end
table.insert(strList, forSeeStr)
end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- Calls _forSee but pulls from the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
timr9agn1p5usnivhmpuuvxx3ptpbpl
281412
281411
2016-05-05T17:16:49Z
en>Nihiltres
0
Removed options.disambiguator and moved its uses to new mHatnote.disambiguate()
281412
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " "
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if string.find(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and string.find(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
-- Stringifies a list with "and"
function p.andList (list)
return stringifyList(list, {conjunction = "and"})
end
-- Stringifies a list with "or"
function p.orList (list)
return stringifyList(list, {conjunction = "or"})
end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forseeForm = 'For %s, see %s.',
punctuationCollapseReplacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
}
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Rows are tables of a "use" string and a "pages" table of pagename strings
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- If there's not at least one page listed, use the default.
table.insert(forRow.pages, args[i + 1] or mHatnote.disambiguate(options.title))
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Takes a table as formatted by forSeeArgsToTable and stringifies it
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table")
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Format each for-see item and make a table containing them
local strList = {}
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(mHatnote.formatPages(unpack(v.pages))) or
mHatnote._formatLink(mHatnote.disambiguate(options.title))
local forSeeStr = string.format(options.forseeForm, useStr, pagesStr)
for k, v in pairs(options.punctuationCollapseReplacements) do
forSeeStr = string.gsub(forSeeStr, k, v)
end
table.insert(strList, forSeeStr)
end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- Calls _forSee but pulls from the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
s68b9lvm2229rqay2ittsrlnhy71bho
281413
281412
2016-05-06T21:34:09Z
en>Nihiltres
0
Protected "[[Module:Hatnote list]]": [[WP:High-risk templates|High-risk Lua module]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
281412
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " "
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if string.find(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and string.find(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
-- Stringifies a list with "and"
function p.andList (list)
return stringifyList(list, {conjunction = "and"})
end
-- Stringifies a list with "or"
function p.orList (list)
return stringifyList(list, {conjunction = "or"})
end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forseeForm = 'For %s, see %s.',
punctuationCollapseReplacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
}
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Rows are tables of a "use" string and a "pages" table of pagename strings
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, use the default,
-- and break at the end of this loop-through.
forRow.use = args[i] or options.otherText
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- If there's not at least one page listed, use the default.
table.insert(forRow.pages, args[i + 1] or mHatnote.disambiguate(options.title))
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Takes a table as formatted by forSeeArgsToTable and stringifies it
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table")
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Format each for-see item and make a table containing them
local strList = {}
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(mHatnote.formatPages(unpack(v.pages))) or
mHatnote._formatLink(mHatnote.disambiguate(options.title))
local forSeeStr = string.format(options.forseeForm, useStr, pagesStr)
for k, v in pairs(options.punctuationCollapseReplacements) do
forSeeStr = string.gsub(forSeeStr, k, v)
end
table.insert(strList, forSeeStr)
end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- Calls _forSee but pulls from the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
s68b9lvm2229rqay2ittsrlnhy71bho
281414
281413
2016-05-11T17:32:24Z
en>Nihiltres
0
Removed default texts set in forSeeArgsToTable(); unset items in table are now empty rather than filled with defaults; defaults are filled in forSeeTableToString().
281414
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " "
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if string.find(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and string.find(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
-- Stringifies a list with "and"
function p.andList (list)
return stringifyList(list, {conjunction = "and"})
end
-- Stringifies a list with "or"
function p.orList (list)
return stringifyList(list, {conjunction = "or"})
end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forseeForm = 'For %s, see %s.',
punctuationCollapseReplacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
}
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Rows are tables of a "use" string and a "pages" table of pagename strings
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, leave empty for
-- defaulting elsewhere, and break at the end of this loop-through.
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Try to insert first pages item; empty is ignored/defaulted elsewhere
table.insert(forRow.pages, args[i + 1])
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Takes a table as formatted by forSeeArgsToTable and stringifies it
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table")
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Format each for-see item and make a table containing them
local strList = {}
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(mHatnote.formatPages(unpack(v.pages))) or
mHatnote._formatLink(mHatnote.disambiguate(options.title))
local forSeeStr = string.format(options.forseeForm, useStr, pagesStr)
for k, v in pairs(options.punctuationCollapseReplacements) do
forSeeStr = string.gsub(forSeeStr, k, v)
end
table.insert(strList, forSeeStr)
end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- Calls _forSee but pulls from the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
74twnvheim2oxz4dxcnj0nuhfsjhlhb
281415
281414
2016-05-12T15:25:34Z
en>Nihiltres
0
Updated with "formatted" option for andList and orList from sandbox
281415
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then list = mHatnote.formatPages(unpack(list)) end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if string.find(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and string.find(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
-- Stringifies a list with "and"
function p.andList (list, formatted)
return stringifyList(list, {conjunction = "and", formatted = formatted})
end
-- Stringifies a list with "or"
function p.orList (list, formatted)
return stringifyList(list, {conjunction = "or", formatted = formatted})
end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forseeForm = 'For %s, see %s.',
punctuationCollapseReplacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
}
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Rows are tables of a "use" string and a "pages" table of pagename strings
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, leave empty for
-- defaulting elsewhere, and break at the end of this loop-through.
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Try to insert first pages item; empty is ignored/defaulted elsewhere
table.insert(forRow.pages, args[i + 1])
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Takes a table as formatted by forSeeArgsToTable and stringifies it
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table")
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Format each for-see item and make a table containing them
local strList = {}
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(mHatnote.formatPages(unpack(v.pages))) or
mHatnote._formatLink(mHatnote.disambiguate(options.title))
local forSeeStr = string.format(options.forseeForm, useStr, pagesStr)
for k, v in pairs(options.punctuationCollapseReplacements) do
forSeeStr = string.gsub(forSeeStr, k, v)
end
table.insert(strList, forSeeStr)
end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- Calls _forSee but pulls from the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
tpi1sbltncyrl3vho9a2im9q1qa320m
281416
281415
2016-05-12T15:46:53Z
en>Nihiltres
0
Actually applied andList's formatted option in forSeeTableToString
281416
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then list = mHatnote.formatPages(unpack(list)) end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if string.find(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and string.find(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
-- Stringifies a list with "and"
function p.andList (list, formatted)
return stringifyList(list, {conjunction = "and", formatted = formatted})
end
-- Stringifies a list with "or"
function p.orList (list, formatted)
return stringifyList(list, {conjunction = "or", formatted = formatted})
end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forseeForm = 'For %s, see %s.',
punctuationCollapseReplacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
}
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list
-- forTable is the wrapper table, with forRow rows
-- Rows are tables of a "use" string and a "pages" table of pagename strings
local forTable = {}
local i = from
local terminated = false
-- Repeat to generate and append each row
repeat
-- New empty row
local forRow = {}
-- If there's a blank use, assume the list's ended, leave empty for
-- defaulting elsewhere, and break at the end of this loop-through.
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Try to insert first pages item; empty is ignored/defaulted elsewhere
table.insert(forRow.pages, args[i + 1])
-- If the option after next is "and", do an inner loop where we collect
-- items following "and"'s until the "and"'s stop. If there's a blank
-- where we'd expect an item, ignore it: "1|and||and|3" → {1, 3}
while args[i + 2] == 'and' do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to the next "and"
i = i + 2
end
-- Increment to the next use
i = i + 2
-- Add the row to the table
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Takes a table as formatted by forSeeArgsToTable and stringifies it
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table")
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Format each for-see item and make a table containing them
local strList = {}
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(v.pages, true) or
mHatnote._formatLink(mHatnote.disambiguate(options.title))
local forSeeStr = string.format(options.forseeForm, useStr, pagesStr)
for k, v in pairs(options.punctuationCollapseReplacements) do
forSeeStr = string.gsub(forSeeStr, k, v)
end
table.insert(strList, forSeeStr)
end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank or whitespace values; those should be filtered. Ignores
-- arguments less than "from", and named arguments.
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- Calls _forSee but pulls from the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
qv1v5pcbwrs5u9089mi6201aslafx4y
281417
281416
2016-06-25T06:18:53Z
en>Nihiltres
0
Updated from sandbox
281417
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then list = mHatnote.formatPages(unpack(list)) end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
--searches display text only
function searchDisp(t, f)
return string.find(string.sub(t, (string.find(t, '|') or 0) + 1), f)
end
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return conjList("and", ...) end
function p.orList (...) return conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.'
}
--Collapses duplicate punctuation
function punctuationCollapse (text)
local replacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
return text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table")
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(v.pages, true) or
mHatnote._formatLink(mHatnote.disambiguate(options.title))
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
9xow4swjoccg1c9n9r6xhfvxgci3xr1
281418
281417
2018-03-31T10:18:42Z
en>Galobtter
0
add "extratext" functionality to "ForSee" to allow text to be added after it
281418
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then list = mHatnote.formatPages(unpack(list)) end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
--searches display text only
function searchDisp(t, f)
return string.find(string.sub(t, (string.find(t, '|') or 0) + 1), f)
end
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return conjList("and", ...) end
function p.orList (...) return conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation
function punctuationCollapse (text)
local replacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
return text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(v.pages, true) or mHatnote._formatLink(mHatnote.disambiguate(options.title))
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
table.insert(strList, options.extratext)
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
5mq4tmbuv2ywdlcbbtatlxwlp5zlmt9
281419
281418
2018-04-03T05:16:55Z
en>Galobtter
0
add "." and collapse punctuation for extratext
281419
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then list = mHatnote.formatPages(unpack(list)) end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
--searches display text only
function searchDisp(t, f)
return string.find(string.sub(t, (string.find(t, '|') or 0) + 1), f)
end
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return conjList("and", ...) end
function p.orList (...) return conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation
function punctuationCollapse (text)
local replacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
return text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(v.pages, true) or mHatnote._formatLink(mHatnote.disambiguate(options.title))
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
table.insert(strList, punctuationCollapse(options.extratext..'.'))
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
r3zk9de7beftx9t5u90wa3ionpzwo70
281420
281419
2018-04-03T05:19:54Z
en>Galobtter
0
fix
281420
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then list = mHatnote.formatPages(unpack(list)) end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
--searches display text only
function searchDisp(t, f)
return string.find(string.sub(t, (string.find(t, '|') or 0) + 1), f)
end
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return conjList("and", ...) end
function p.orList (...) return conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation
function punctuationCollapse (text)
local replacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
return text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(v.pages, true) or mHatnote._formatLink(mHatnote.disambiguate(options.title))
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
2hmyydi47hep0hexbh8sw7ybxc07m5k
281421
281420
2020-06-16T14:57:17Z
en>Mr. Stradivarius
0
Update with new _formatLink syntax for [[Module:Hatnote]]; using the [[Module:Hatnote/sandbox]] for now, but will switch back to [[Module:Hatnote]] in a short while. Also make "searchDisp" a local variable instead of a global.
281421
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote/sandbox')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then list = mHatnote.formatPages(unpack(list)) end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
--searches display text only
local function searchDisp(t, f)
return string.find(string.sub(t, (string.find(t, '|') or 0) + 1), f)
end
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return conjList("and", ...) end
function p.orList (...) return conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation
function punctuationCollapse (text)
local replacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
return text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(v.pages, true) or mHatnote._formatLink{link = mHatnote.disambiguate(options.title)}
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
9520t0dmcy0y52fg9350rhpmekdlmpq
281422
281421
2020-06-16T15:02:40Z
en>Mr. Stradivarius
0
switch back to [[Module:Hatnote]] instead of [[Module:Hatnote/sandbox]]
281422
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then list = mHatnote.formatPages(unpack(list)) end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
--searches display text only
local function searchDisp(t, f)
return string.find(string.sub(t, (string.find(t, '|') or 0) + 1), f)
end
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return conjList("and", ...) end
function p.orList (...) return conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation
function punctuationCollapse (text)
local replacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
return text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(v.pages, true) or mHatnote._formatLink{link = mHatnote.disambiguate(options.title)}
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
fqf9dfriof1mvsvpj4ulnui0b5k1dz1
281423
281422
2021-12-26T17:57:33Z
en>Nihiltres
0
Updated from sandbox to use [[Module:Format link]], and export conjList (credit Jackmcbarn), and tag functions local, and extract searchDisp as a nested function
281423
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mFormatLink = require('Module:Format link')
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
--Searches display text only
local function searchDisp(haystack, needle)
return string.find(
string.sub(haystack, (string.find(haystack, '|') or 0) + 1), needle
)
end
-- Stringifies a list generically; probably shouldn't be used directly
local function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then
list = mFormatLink.formatPages(
{categorizeMissing = mHatnote.missingTargetCat}, list
)
end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function p.conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return p.conjList("and", ...) end
function p.orList (...) return p.conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation
local function punctuationCollapse (text)
local replacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
return text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr =
p.andList(v.pages, true) or
mFormatLink._formatLink{
categorizeMissing = mHatnote.missingTargetCat,
link = mHatnote.disambiguate(options.title)
}
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
octqbcvqrobtndgnn8u78tg2tsemf14
281424
281423
2021-12-26T19:11:47Z
en>Matthiaspaul
0
Undid revision 1062157581 by [[Special:Contributions/Nihiltres|Nihiltres]] ([[User talk:Nihiltres|talk]]) Temp undo as [[Template:See also]] now throws errors like "Lua error: bad argument #1 to 'title.new' (number or string expected, got nil)." (for example in article [[Bronshtein and Semendyayev]]).
281424
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
-- Stringifies a list generically; probably shouldn't be used directly
function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then list = mHatnote.formatPages(unpack(list)) end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
--searches display text only
local function searchDisp(t, f)
return string.find(string.sub(t, (string.find(t, '|') or 0) + 1), f)
end
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return conjList("and", ...) end
function p.orList (...) return conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation
function punctuationCollapse (text)
local replacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
return text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr = p.andList(v.pages, true) or mHatnote._formatLink{link = mHatnote.disambiguate(options.title)}
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
fqf9dfriof1mvsvpj4ulnui0b5k1dz1
281425
281424
2021-12-26T20:05:24Z
en>Nihiltres
0
Undid revision 1062166786 by [[Special:Contributions/Matthiaspaul|Matthiaspaul]] ([[User talk:Matthiaspaul|talk]]); should be fixed now, and if not, please ping me with examples as I couldn't reproduce the original error
281425
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mFormatLink = require('Module:Format link')
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
--Searches display text only
local function searchDisp(haystack, needle)
return string.find(
string.sub(haystack, (string.find(haystack, '|') or 0) + 1), needle
)
end
-- Stringifies a list generically; probably shouldn't be used directly
local function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then
list = mFormatLink.formatPages(
{categorizeMissing = mHatnote.missingTargetCat}, list
)
end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function p.conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return p.conjList("and", ...) end
function p.orList (...) return p.conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation
local function punctuationCollapse (text)
local replacements = {
["%.%.$"] = ".",
["%?%.$"] = "?",
["%!%.$"] = "!",
["%.%]%]%.$"] = ".]]",
["%?%]%]%.$"] = "?]]",
["%!%]%]%.$"] = "!]]"
}
for k, v in pairs(replacements) do text = string.gsub(text, k, v) end
return text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr =
p.andList(v.pages, true) or
mFormatLink._formatLink{
categorizeMissing = mHatnote.missingTargetCat,
link = mHatnote.disambiguate(options.title)
}
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
octqbcvqrobtndgnn8u78tg2tsemf14
281426
281425
2023-11-13T21:00:31Z
en>Nihiltres
0
Updated from sandbox: added support for punctuation collapse when text is italicized. The update's content includes changes by users Johnuniq, Dexxor, and Nihiltres.
281426
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mFormatLink = require('Module:Format link')
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
--Searches display text only
local function searchDisp(haystack, needle)
return string.find(
string.sub(haystack, (string.find(haystack, '|') or 0) + 1), needle
)
end
-- Stringifies a list generically; probably shouldn't be used directly
local function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then
list = mFormatLink.formatPages(
{categorizeMissing = mHatnote.missingTargetCat}, list
)
end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function p.conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return p.conjList("and", ...) end
function p.orList (...) return p.conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation at end of string, ignoring italics and links
local function punctuationCollapse (text)
return text:match("[.?!]('?)%1(%]?)%2%.$") and text:sub(1, -2) or text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr =
p.andList(v.pages, true) or
mFormatLink._formatLink{
categorizeMissing = mHatnote.missingTargetCat,
link = mHatnote.disambiguate(options.title)
}
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
3c31ivxc2t731ceyfvqcqlnp9acgg3s
281427
281426
2026-06-07T17:49:51Z
Ameisenigel
44
24 revisions imported from [[:en:Module:Hatnote_list]]: Request at [[WF:AN]]
281426
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Hatnote list --
-- --
-- This module produces and formats lists for use in hatnotes. In particular, --
-- it implements the for-see list, i.e. lists of "For X, see Y" statements, --
-- as used in {{about}}, {{redirect}}, and their variants. Also introduced --
-- are andList & orList helpers for formatting lists with those conjunctions. --
--------------------------------------------------------------------------------
local mArguments --initialize lazily
local mFormatLink = require('Module:Format link')
local mHatnote = require('Module:Hatnote')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local p = {}
--------------------------------------------------------------------------------
-- List stringification helper functions
--
-- These functions are used for stringifying lists, usually page lists inside
-- the "Y" portion of "For X, see Y" for-see items.
--------------------------------------------------------------------------------
--default options table used across the list stringification functions
local stringifyListDefaultOptions = {
conjunction = "and",
separator = ",",
altSeparator = ";",
space = " ",
formatted = false
}
--Searches display text only
local function searchDisp(haystack, needle)
return string.find(
string.sub(haystack, (string.find(haystack, '|') or 0) + 1), needle
)
end
-- Stringifies a list generically; probably shouldn't be used directly
local function stringifyList(list, options)
-- Type-checks, defaults, and a shortcut
checkType("stringifyList", 1, list, "table")
if #list == 0 then return nil end
checkType("stringifyList", 2, options, "table", true)
options = options or {}
for k, v in pairs(stringifyListDefaultOptions) do
if options[k] == nil then options[k] = v end
end
local s = options.space
-- Format the list if requested
if options.formatted then
list = mFormatLink.formatPages(
{categorizeMissing = mHatnote.missingTargetCat}, list
)
end
-- Set the separator; if any item contains it, use the alternate separator
local separator = options.separator
for k, v in pairs(list) do
if searchDisp(v, separator) then
separator = options.altSeparator
break
end
end
-- Set the conjunction, apply Oxford comma, and force a comma if #1 has "§"
local conjunction = s .. options.conjunction .. s
if #list == 2 and searchDisp(list[1], "§") or #list > 2 then
conjunction = separator .. conjunction
end
-- Return the formatted string
return mw.text.listToText(list, separator .. s, conjunction)
end
--DRY function
function p.conjList (conj, list, fmt)
return stringifyList(list, {conjunction = conj, formatted = fmt})
end
-- Stringifies lists with "and" or "or"
function p.andList (...) return p.conjList("and", ...) end
function p.orList (...) return p.conjList("or", ...) end
--------------------------------------------------------------------------------
-- For see
--
-- Makes a "For X, see [[Y]]." list from raw parameters. Intended for the
-- {{about}} and {{redirect}} templates and their variants.
--------------------------------------------------------------------------------
--default options table used across the forSee family of functions
local forSeeDefaultOptions = {
andKeyword = 'and',
title = mw.title.getCurrentTitle().text,
otherText = 'other uses',
forSeeForm = 'For %s, see %s.',
}
--Collapses duplicate punctuation at end of string, ignoring italics and links
local function punctuationCollapse (text)
return text:match("[.?!]('?)%1(%]?)%2%.$") and text:sub(1, -2) or text
end
-- Structures arguments into a table for stringification, & options
function p.forSeeArgsToTable (args, from, options)
-- Type-checks and defaults
checkType("forSeeArgsToTable", 1, args, 'table')
checkType("forSeeArgsToTable", 2, from, 'number', true)
from = from or 1
checkType("forSeeArgsToTable", 3, options, 'table', true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- maxArg's gotten manually because getArgs() and table.maxn aren't friends
local maxArg = 0
for k, v in pairs(args) do
if type(k) == 'number' and k > maxArg then maxArg = k end
end
-- Structure the data out from the parameter list:
-- * forTable is the wrapper table, with forRow rows
-- * Rows are tables of a "use" string & a "pages" table of pagename strings
-- * Blanks are left empty for defaulting elsewhere, but can terminate list
local forTable = {}
local i = from
local terminated = false
-- If there is extra text, and no arguments are given, give nil value
-- to not produce default of "For other uses, see foo (disambiguation)"
if options.extratext and i > maxArg then return nil end
-- Loop to generate rows
repeat
-- New empty row
local forRow = {}
-- On blank use, assume list's ended & break at end of this loop
forRow.use = args[i]
if not args[i] then terminated = true end
-- New empty list of pages
forRow.pages = {}
-- Insert first pages item if present
table.insert(forRow.pages, args[i + 1])
-- If the param after next is "and", do inner loop to collect params
-- until the "and"'s stop. Blanks are ignored: "1|and||and|3" → {1, 3}
while args[i + 2] == options.andKeyword do
if args[i + 3] then
table.insert(forRow.pages, args[i + 3])
end
-- Increment to next "and"
i = i + 2
end
-- Increment to next use
i = i + 2
-- Append the row
table.insert(forTable, forRow)
until terminated or i > maxArg
return forTable
end
-- Stringifies a table as formatted by forSeeArgsToTable
function p.forSeeTableToString (forSeeTable, options)
-- Type-checks and defaults
checkType("forSeeTableToString", 1, forSeeTable, "table", true)
checkType("forSeeTableToString", 2, options, "table", true)
options = options or {}
for k, v in pairs(forSeeDefaultOptions) do
if options[k] == nil then options[k] = v end
end
-- Stringify each for-see item into a list
local strList = {}
if forSeeTable then
for k, v in pairs(forSeeTable) do
local useStr = v.use or options.otherText
local pagesStr =
p.andList(v.pages, true) or
mFormatLink._formatLink{
categorizeMissing = mHatnote.missingTargetCat,
link = mHatnote.disambiguate(options.title)
}
local forSeeStr = string.format(options.forSeeForm, useStr, pagesStr)
forSeeStr = punctuationCollapse(forSeeStr)
table.insert(strList, forSeeStr)
end
end
if options.extratext then table.insert(strList, punctuationCollapse(options.extratext..'.')) end
-- Return the concatenated list
return table.concat(strList, ' ')
end
-- Produces a "For X, see [[Y]]" string from arguments. Expects index gaps
-- but not blank/whitespace values. Ignores named args and args < "from".
function p._forSee (args, from, options)
local forSeeTable = p.forSeeArgsToTable(args, from, options)
return p.forSeeTableToString(forSeeTable, options)
end
-- As _forSee, but uses the frame.
function p.forSee (frame, from, options)
mArguments = require('Module:Arguments')
return p._forSee(mArguments.getArgs(frame), from, options)
end
return p
3c31ivxc2t731ceyfvqcqlnp9acgg3s
Module:Format link
828
85259
281428
2021-12-19T03:58:26Z
en>Nihiltres
0
Presumably doesn't work yet, but … basic code copied from [[Module:Hatnote]], which includes contributions from Mr. Stradivarius, SMcCandlish, Codename Lisa, Ahecht, Qed237, Nihiltres, JJMC89, Pppery, and Izno.
281428
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = string.format('§ %s', section)
elseif section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
od46kxkjsgaj9ul5onjt6m6bm1nn3jg
281429
281428
2021-12-19T04:04:29Z
en>Nihiltres
0
Further copied getArgs from [[Module:Hatnote]] and organized into helper and main functions
281429
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = string.format('§ %s', section)
elseif section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
f9111s19hkulex65gqy0i1fz1wlkip4
281430
281429
2021-12-19T21:52:10Z
en>Nihiltres
0
Add missing "return p"
281430
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = string.format('§ %s', section)
elseif section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
return p
c1l0iefks9euljhzpp86s280m1lgk96
281431
281430
2021-12-19T21:53:44Z
en>Nihiltres
0
Copied function removeInitialColon from [[Module:Hatnote]]
281431
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return string.format('[[:%s]]', parsed.link)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = string.format('§ %s', section)
elseif section then
display = string.format('%s § %s', page, section)
else
display = page
end
end
return string.format('[[:%s|%s]]', parsed.link, display)
end
return p
fnqndguqay0a31sdeb0el83n3ystv1n
281432
281431
2021-12-19T22:06:31Z
en>Nihiltres
0
Integrate improvements from [[Module:Hatnote/sandbox]], in particular use of mw.ustring by Izno and my own categoryMissing stuff
281432
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'categorizeMissing',
options.categorizeMissing,
'string',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
--Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
if not mw.title.new(parsed.page).exists then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = mw.ustring.format('§ %s', section)
elseif section then
display = mw.ustring.format('%s § %s', page, section)
else
display = page
end
end
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
return p
azyrbezvu055xl5eqp8uqb4pleyxylz
281433
281432
2021-12-21T18:01:45Z
en>Nihiltres
0
Added formatPages analogue
281433
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'categorizeMissing',
options.categorizeMissing,
'string',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
--Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
if not mw.title.new(parsed.page).exists then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = mw.ustring.format('§ %s', section)
elseif section then
display = mw.ustring.format('%s § %s', page, section)
else
display = page
end
end
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
tqqs56lmcfsjk0mx9rp2gzpkz070fwe
281434
281433
2021-12-23T18:32:29Z
en>Nihiltres
0
Protected "[[Module:Format link]]": [[WP:High-risk templates|Highly visible template]]: Will soon be used in [[Template:Format link]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
281433
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return p.makeWikitextError(
'no link specified',
'Template:Format link#Errors',
args.category
)
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'categorizeMissing',
options.categorizeMissing,
'string',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
--Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
if not mw.title.new(parsed.page).exists then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = mw.ustring.format('§ %s', section)
elseif section then
display = mw.ustring.format('%s § %s', page, section)
else
display = page
end
end
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
tqqs56lmcfsjk0mx9rp2gzpkz070fwe
281435
281434
2021-12-23T18:46:06Z
en>Nihiltres
0
Replaced broken use of makeWikitextError from Module:Hatnote
281435
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return
'<strong class="error">'..
'Error: no link specified ([[Template:Format link#Errors|help]]).'..
'</strong>'
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'categorizeMissing',
options.categorizeMissing,
'string',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
--Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
if not mw.title.new(parsed.page).exists then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = mw.ustring.format('§ %s', section)
elseif section then
display = mw.ustring.format('%s § %s', page, section)
else
display = page
end
end
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
4ck3r2s23l22xd54iccmq52s7s43adg
281436
281435
2021-12-26T18:25:22Z
en>Nihiltres
0
Hotfix: disregard interwiki links as "not existing" for "missing target" categorization
281436
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return
'<strong class="error">'..
'Error: no link specified ([[Template:Format link#Errors|help]]).'..
'</strong>'
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'categorizeMissing',
options.categorizeMissing,
'string',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
--Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
local title = mw.title.new(parsed.page)
if title.isLocal and (not title.exists) then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = mw.ustring.format('§ %s', section)
elseif section then
display = mw.ustring.format('%s § %s', page, section)
else
display = page
end
end
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
fhppjzhz55in4ma1osqiz21970q0xvj
281437
281436
2021-12-26T18:30:11Z
en>Nihiltres
0
Tweak hotfix further to use isExternal instead of isLocal
281437
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return
'<strong class="error">'..
'Error: no link specified ([[Template:Format link#Errors|help]]).'..
'</strong>'
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'categorizeMissing',
options.categorizeMissing,
'string',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
--Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
local title = mw.title.new(parsed.page)
if (not title.isExternal) and (not title.exists) then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = mw.ustring.format('§ %s', section)
elseif section then
display = mw.ustring.format('%s § %s', page, section)
else
display = page
end
end
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
exwo04hawwsgawuouz83hdkllfsqlsf
281438
281437
2021-12-26T20:01:59Z
en>Nihiltres
0
Added extra condition to check if title object is null
281438
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return
'<strong class="error">'..
'Error: no link specified ([[Template:Format link#Errors|help]]).'..
'</strong>'
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'categorizeMissing',
options.categorizeMissing,
'string',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
--Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
local title = mw.title.new(parsed.page)
if title and (not title.isExternal) and (not title.exists) then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = mw.ustring.format('§ %s', section)
elseif section then
display = mw.ustring.format('%s § %s', page, section)
else
display = page
end
end
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
aoplva9g3csu9312t83ntqennxuweak
281439
281438
2021-12-26T20:07:48Z
en>Nihiltres
0
An extra layer of nil check
281439
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function italicize(s)
-- Italicize a string.
return '<i>' .. s .. '</i>'
end
local function maybeItalicize(s, shouldItalicize)
-- italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return italicize(s)
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1]
if not link then
return
'<strong class="error">'..
'Error: no link specified ([[Template:Format link#Errors|help]]).'..
'</strong>'
end
return p._formatLink{
link = link,
display = args[2],
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
checkTypeForNamedArg('_formatLink', 'link', options.link, 'string', false)
checkTypeForNamedArg(
'_formatLink',
'display',
options.display,
'string',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizePage',
options.italicizePage,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'italicizeSection',
options.italicizeSection,
'boolean',
true
)
checkTypeForNamedArg(
'_formatLink',
'categorizeMissing',
options.categorizeMissing,
'string',
true
)
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
--Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
local title = nil
if parsed.page then title = mw.title.new(parsed.page) end
if title and (not title.isExternal) and (not title.exists) then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
-- Deal with the case where we don't have to pipe the link
if not display and not parsed.section and not options.italicizePage then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
end
-- Find the display text for piped links
if not display then
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if not page then
display = mw.ustring.format('§ %s', section)
elseif section then
display = mw.ustring.format('%s § %s', page, section)
else
display = page
end
end
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
jo6gyjvn11gy381f56st02s5twdtk8n
281440
281439
2022-01-06T04:38:04Z
en>Nihiltres
0
Updated from sandbox with new target override functionality
281440
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local mError -- lazily initialise [[Module:Error]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function maybeItalicize(s, shouldItalicize)
-- Italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return '<i>' .. s .. '</i>'
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
local function formatDisplay(parsed, options)
-- Formats a display string based on a parsed link table (matching the
-- output of parseLink) and an options table (matching the input options for
-- _formatLink).
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if (not section) then
return page
elseif (not page) then
return mw.ustring.format('§ %s', section)
else
return mw.ustring.format('%s § %s', page, section)
end
end
local function missingArgError(target)
mError = require('Module:Error')
return mError._error{message =
'Error: no link or target specified! ([[' .. target .. '#Errors|help]])'
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1] or args.link
local target = args[3] or args.target
if not (link or target) then
return missingArgError('Template:Format link')
end
return p._formatLink{
link = link,
display = args[2] or args.display,
target = target,
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
local function check(key, expectedType) --for brevity
checkTypeForNamedArg(
'_formatLink', key, options[key], expectedType or 'string', true
)
end
check('link')
check('display')
check('target')
check('italicizePage', 'boolean')
check('italicizeSection', 'boolean')
check('categorizeMissing')
-- Normalize link and target and check that at least one is present
if options.link == '' then options.link = nil end
if options.target == '' then options.target = nil end
if not (options.link or options.target) then
return missingArgError('Module:Format link')
end
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
-- Find the display text
if not display then display = formatDisplay(parsed, options) end
-- Handle the target option if present
if options.target then
local parsedTarget = parseLink(options.target)
parsed.link = parsedTarget.link
parsed.page = parsedTarget.page
end
-- Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
local title = nil
if parsed.page then title = mw.title.new(parsed.page) end
if title and (not title.isExternal) and (not title.exists) then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
-- Format the result as a link
if parsed.link == display then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
else
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
m3qubxxalvfl4psz4xdswsyrhv8jxc8
281441
281440
2022-01-09T02:48:07Z
en>Hike395
0
incorrect entry point
281441
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local mError -- lazily initialise [[Module:Error]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function maybeItalicize(s, shouldItalicize)
-- Italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return '<i>' .. s .. '</i>'
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
local function formatDisplay(parsed, options)
-- Formats a display string based on a parsed link table (matching the
-- output of parseLink) and an options table (matching the input options for
-- _formatLink).
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if (not section) then
return page
elseif (not page) then
return mw.ustring.format('§ %s', section)
else
return mw.ustring.format('%s § %s', page, section)
end
end
local function missingArgError(target)
mError = require('Module:Error')
return mError.error{message =
'Error: no link or target specified! ([[' .. target .. '#Errors|help]])'
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1] or args.link
local target = args[3] or args.target
if not (link or target) then
return missingArgError('Template:Format link')
end
return p._formatLink{
link = link,
display = args[2] or args.display,
target = target,
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
local function check(key, expectedType) --for brevity
checkTypeForNamedArg(
'_formatLink', key, options[key], expectedType or 'string', true
)
end
check('link')
check('display')
check('target')
check('italicizePage', 'boolean')
check('italicizeSection', 'boolean')
check('categorizeMissing')
-- Normalize link and target and check that at least one is present
if options.link == '' then options.link = nil end
if options.target == '' then options.target = nil end
if not (options.link or options.target) then
return missingArgError('Module:Format link')
end
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
-- Find the display text
if not display then display = formatDisplay(parsed, options) end
-- Handle the target option if present
if options.target then
local parsedTarget = parseLink(options.target)
parsed.link = parsedTarget.link
parsed.page = parsedTarget.page
end
-- Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
local title = nil
if parsed.page then title = mw.title.new(parsed.page) end
if title and (not title.isExternal) and (not title.exists) then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
-- Format the result as a link
if parsed.link == display then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
else
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
kqzzmt53imtxeq4z2wtd8gb4aoahnkl
281442
281441
2022-10-04T13:37:11Z
en>Pppery
0
Avoid Lua erroring when we run out of expensive parser function calls
281442
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local mError -- lazily initialise [[Module:Error]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function maybeItalicize(s, shouldItalicize)
-- Italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return '<i>' .. s .. '</i>'
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
local function formatDisplay(parsed, options)
-- Formats a display string based on a parsed link table (matching the
-- output of parseLink) and an options table (matching the input options for
-- _formatLink).
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if (not section) then
return page
elseif (not page) then
return mw.ustring.format('§ %s', section)
else
return mw.ustring.format('%s § %s', page, section)
end
end
local function missingArgError(target)
mError = require('Module:Error')
return mError.error{message =
'Error: no link or target specified! ([[' .. target .. '#Errors|help]])'
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1] or args.link
local target = args[3] or args.target
if not (link or target) then
return missingArgError('Template:Format link')
end
return p._formatLink{
link = link,
display = args[2] or args.display,
target = target,
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
local function check(key, expectedType) --for brevity
checkTypeForNamedArg(
'_formatLink', key, options[key], expectedType or 'string', true
)
end
check('link')
check('display')
check('target')
check('italicizePage', 'boolean')
check('italicizeSection', 'boolean')
check('categorizeMissing')
-- Normalize link and target and check that at least one is present
if options.link == '' then options.link = nil end
if options.target == '' then options.target = nil end
if not (options.link or options.target) then
return missingArgError('Module:Format link')
end
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
-- Find the display text
if not display then display = formatDisplay(parsed, options) end
-- Handle the target option if present
if options.target then
local parsedTarget = parseLink(options.target)
parsed.link = parsedTarget.link
parsed.page = parsedTarget.page
end
-- Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
local title = nil
if parsed.page then title = mw.title.new(parsed.page) end
if title and (not title.isExternal) then
local success, exists = pcall(function() return title.exists end)
if success and not exists then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
end
-- Format the result as a link
if parsed.link == display then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
else
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
252hp8jk2qv051cngf0qjx0ljcf0bof
281443
281442
2026-06-07T17:51:11Z
Ameisenigel
44
15 revisions imported from [[:en:Module:Format_link]]: Request at [[WF:AN]]
281442
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Format link
--
-- Makes a wikilink from the given link and display values. Links are escaped
-- with colons if necessary, and links to sections are detected and displayed
-- with " § " as a separator rather than the standard MediaWiki "#". Used in
-- the {{format link}} template.
--------------------------------------------------------------------------------
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local mArguments -- lazily initialise [[Module:Arguments]]
local mError -- lazily initialise [[Module:Error]]
local yesno -- lazily initialise [[Module:Yesno]]
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function getArgs(frame)
-- Fetches the arguments from the parent frame. Whitespace is trimmed and
-- blanks are removed.
mArguments = require('Module:Arguments')
return mArguments.getArgs(frame, {parentOnly = true})
end
local function removeInitialColon(s)
-- Removes the initial colon from a string, if present.
return s:match('^:?(.*)')
end
local function maybeItalicize(s, shouldItalicize)
-- Italicize s if s is a string and the shouldItalicize parameter is true.
if s and shouldItalicize then
return '<i>' .. s .. '</i>'
else
return s
end
end
local function parseLink(link)
-- Parse a link and return a table with the link's components.
-- These components are:
-- - link: the link, stripped of any initial colon (always present)
-- - page: the page name (always present)
-- - section: the page name (may be nil)
-- - display: the display text, if manually entered after a pipe (may be nil)
link = removeInitialColon(link)
-- Find whether a faux display value has been added with the {{!}} magic
-- word.
local prePipe, display = link:match('^(.-)|(.*)$')
link = prePipe or link
-- Find the page, if it exists.
-- For links like [[#Bar]], the page will be nil.
local preHash, postHash = link:match('^(.-)#(.*)$')
local page
if not preHash then
-- We have a link like [[Foo]].
page = link
elseif preHash ~= '' then
-- We have a link like [[Foo#Bar]].
page = preHash
end
-- Find the section, if it exists.
local section
if postHash and postHash ~= '' then
section = postHash
end
return {
link = link,
page = page,
section = section,
display = display,
}
end
local function formatDisplay(parsed, options)
-- Formats a display string based on a parsed link table (matching the
-- output of parseLink) and an options table (matching the input options for
-- _formatLink).
local page = maybeItalicize(parsed.page, options.italicizePage)
local section = maybeItalicize(parsed.section, options.italicizeSection)
if (not section) then
return page
elseif (not page) then
return mw.ustring.format('§ %s', section)
else
return mw.ustring.format('%s § %s', page, section)
end
end
local function missingArgError(target)
mError = require('Module:Error')
return mError.error{message =
'Error: no link or target specified! ([[' .. target .. '#Errors|help]])'
}
end
--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------
function p.formatLink(frame)
-- The formatLink export function, for use in templates.
yesno = require('Module:Yesno')
local args = getArgs(frame)
local link = args[1] or args.link
local target = args[3] or args.target
if not (link or target) then
return missingArgError('Template:Format link')
end
return p._formatLink{
link = link,
display = args[2] or args.display,
target = target,
italicizePage = yesno(args.italicizepage),
italicizeSection = yesno(args.italicizesection),
categorizeMissing = args.categorizemissing
}
end
function p._formatLink(options)
-- The formatLink export function, for use in modules.
checkType('_formatLink', 1, options, 'table')
local function check(key, expectedType) --for brevity
checkTypeForNamedArg(
'_formatLink', key, options[key], expectedType or 'string', true
)
end
check('link')
check('display')
check('target')
check('italicizePage', 'boolean')
check('italicizeSection', 'boolean')
check('categorizeMissing')
-- Normalize link and target and check that at least one is present
if options.link == '' then options.link = nil end
if options.target == '' then options.target = nil end
if not (options.link or options.target) then
return missingArgError('Module:Format link')
end
local parsed = parseLink(options.link)
local display = options.display or parsed.display
local catMissing = options.categorizeMissing
local category = ''
-- Find the display text
if not display then display = formatDisplay(parsed, options) end
-- Handle the target option if present
if options.target then
local parsedTarget = parseLink(options.target)
parsed.link = parsedTarget.link
parsed.page = parsedTarget.page
end
-- Test if page exists if a diagnostic category is specified
if catMissing and (mw.ustring.len(catMissing) > 0) then
local title = nil
if parsed.page then title = mw.title.new(parsed.page) end
if title and (not title.isExternal) then
local success, exists = pcall(function() return title.exists end)
if success and not exists then
category = mw.ustring.format('[[Category:%s]]', catMissing)
end
end
end
-- Format the result as a link
if parsed.link == display then
return mw.ustring.format('[[:%s]]%s', parsed.link, category)
else
return mw.ustring.format('[[:%s|%s]]%s', parsed.link, display, category)
end
end
--------------------------------------------------------------------------------
-- Derived convenience functions
--------------------------------------------------------------------------------
function p.formatPages(options, pages)
-- Formats an array of pages using formatLink and the given options table,
-- and returns it as an array. Nil values are not allowed.
local ret = {}
for i, page in ipairs(pages) do
ret[i] = p._formatLink{
link = page,
categorizeMissing = options.categorizeMissing,
italicizePage = options.italicizePage,
italicizeSection = options.italicizeSection
}
end
return ret
end
return p
252hp8jk2qv051cngf0qjx0ljcf0bof
Translations:Wikifunctions:Status updates/2026-06-05/14/de
1198
85260
281445
2026-06-07T18:41:32Z
Ameisenigel
44
Created page with "''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[$1|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[$2|99of9]] erstellt und von [[$3|Feeglgeef]] redaktionell bearbeitet. Danke!''"
281445
wikitext
text/x-wiki
''Jede Woche stellen wir eine Funktion im Detail vor, um die Möglichkeiten von Wikifunctions zu demonstrieren. Die Funktionen der Woche stammen meist von der Community. [[$1|Hier kannst du eine Funktion vorschlagen]]. Die Funktion dieser Woche wurde von [[$2|99of9]] erstellt und von [[$3|Feeglgeef]] redaktionell bearbeitet. Danke!''
szeil66uvnau4ekjgbvycljn4bbkzkx
Translations:Wikifunctions:Status updates/2026-06-05/15/de
1198
85261
281448
2026-06-07T18:44:39Z
Ameisenigel
44
Created page with "Die Funktionen [[$1|Hauptartikel]] und [[$2|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an ..."
281448
wikitext
text/x-wiki
Die Funktionen [[$1|Hauptartikel]] und [[$2|Hauptartikel, komplex]] werden in der Abstrakten Wikipedia nach einer Abschnittsüberschrift verwendet, um auf einen eigenständigen Artikel mit weiterführenden Informationen zu diesem Unterthema zu verweisen. Diese Links ermöglichen es den Lesern, schnell zu den Themen zu gelangen, die sie am meisten interessieren, und sind in den meisten Sprachversionen der Wikipedia weit verbreitet. Unsere Funktionen orientieren sich an [[$3|Template:Main]] der englischsprachigen Wikipedia, die eine (meist kurze) Liste von Zielartikeln sowie optional alternative Beschriftungen für diese Links entgegennimmt und verfügt über einen Parameter zur Steuerung und leichten Anpassung des Verhaltens bei Selbstreferenzierungen. Sie erzeugt einen kurzen, abgesetzten Hinweis, der diesen Link als Hauptartikel für den jeweiligen Unterabschnitt ausweist.
44n7jcr9h422g1m0o2i9h0wfrnt9c6w
Translations:Wikifunctions:Status updates/2026-06-05/16/de
1198
85262
281450
2026-06-07T18:48:13Z
Ameisenigel
44
Created page with "Die Vollversion unserer Funktion [[$1|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[$2|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen je..."
281450
wikitext
text/x-wiki
Die Vollversion unserer Funktion [[$1|Hauptartikel, komplex]] ist so konzipiert, dass sie drei gleichartige Eingaben sowie die Zielsprache für die Ausgabe entgegennimmt. Die Link-Elemente werden als Liste von [[$2|Wikidata-Datenobjekt-Referenzen]] übergeben. Bei den alternativen Bezeichnungen kann es sich nicht einfach um Zeichenketten handeln, da diese nicht in allen Sprachen funktionieren würden. Vorerst akzeptieren wir zwar eine Liste von Objekten, unterstützen jedoch noch keine spezifischen Funktionen für diese Objekte. Zukünftig könnten sie mit Funktionsreferenzen oder anderen Datenstrukturen versehen werden, die die zur Generierung alternativer Bezeichnungen erforderlichen Informationen enthalten. Der Parameter für die Selbstreferenz ist ein boolescher Wert. Die Funktion gibt ein [[$3|HTML-Fragment]] zurück, wie es für die Abstrakte Wikipedia erforderlich ist. Die [[$4|Kompositionsimplementierung]] übernimmt das Umschließen der jeweiligen sprachspezifischen Ausgabe mit einem HTML-div-Element samt Attribut $5, sodass die sprachspezifischen Teilfunktionen lediglich für das Abrufen und Formatieren des Linknamens zuständig sind.
iyit1yix5yhp80dro34cm1ionh0dxet
Translations:Wikifunctions:Status updates/2026-06-05/17/de
1198
85263
281453
2026-06-07T18:59:46Z
Ameisenigel
44
Created page with "Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[$1|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen."
281453
wikitext
text/x-wiki
Meist ist die Komplexität alternativer Bezeichnungen und Selbstreferenzen unnötig. Daher benötigt die einfachere Variante unserer Funktion, [[$1|Hauptartikel]], lediglich eine Liste von Elementen sowie eine Sprache als Argumente. Sie kapselt die komplexe Variante ein und übergibt Standardwerte für die nicht verwendeten Argumente. Diese Version ist im Quellcode der Abstrakten Wikipedia einfacher zu verwenden und zu lesen.
06mgncvzgklmywwqml9xgz7x54vpdzp
Translations:Wikifunctions:Status updates/2026-06-05/18/de
1198
85264
281455
2026-06-07T19:00:05Z
Ameisenigel
44
Created page with "Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[$1|Australien (Q408)]] basieren:"
281455
wikitext
text/x-wiki
Es stehen sechs Tests zur Verfügung, die auf der häufigen Verwendung dieser Funktion zur Strukturierung des abstrakten Artikels [[$1|Australien (Q408)]] basieren:
idlajtq1eznri6jfqfrxqepfvges5xd
Translations:Wikifunctions:Status updates/2026-06-05/19/de
1198
85265
281457
2026-06-07T19:00:33Z
Ameisenigel
44
Created page with "[[$1|''Main article:'' History of Australia]]"
281457
wikitext
text/x-wiki
[[$1|''Main article:'' History of Australia]]
r1q8cq9m7x4qctyj5psew59dli73eg1
Translations:Wikifunctions:Status updates/2026-06-05/20/de
1198
85266
281459
2026-06-07T19:00:36Z
Ameisenigel
44
Created page with "[[$1|''Main articles:'' Geography of Australia and Australian continent]]"
281459
wikitext
text/x-wiki
[[$1|''Main articles:'' Geography of Australia and Australian continent]]
nf4m99bqdkoaepcut451zstrhzcnx0q
Translations:Wikifunctions:Status updates/2026-06-05/21/de
1198
85267
281461
2026-06-07T19:01:24Z
Ameisenigel
44
Created page with "[[$1|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, $2, fehl, der [[$3|Z13464]] betrifft, worauf sich die Komposition verlässt."
281461
wikitext
text/x-wiki
[[$1|''Main articles:'' Australian Government, Politics of Australia and Monarchy of Australia]] schlägt derzeit aufgrund eines bekannten Fehlers, $2, fehl, der [[$3|Z13464]] betrifft, worauf sich die Komposition verlässt.
56ds9qggla97sy2mw7njb17hyxna4hi
Translations:Wikifunctions:Status updates/2026-06-05/22/de
1198
85268
281463
2026-06-07T19:01:43Z
Ameisenigel
44
Created page with "[[$1|''Hoofdartikel:'' Geografie van Australië]] in Belgischem Niederländisch."
281463
wikitext
text/x-wiki
[[$1|''Hoofdartikel:'' Geografie van Australië]] in Belgischem Niederländisch.
agf4v1yhms1sdadhwcb0qpgruylnt3a
Translations:Wikifunctions:Status updates/2026-06-05/23/de
1198
85269
281465
2026-06-07T19:02:03Z
Ameisenigel
44
Created page with "[[$1|''→ Hauptartikel:'' Klima in Australien]] wartet auf eine Implementierung in Deutsch."
281465
wikitext
text/x-wiki
[[$1|''→ Hauptartikel:'' Klima in Australien]] wartet auf eine Implementierung in Deutsch.
0t2lo1vcpyi4zz03pcql5j57p0pcasp
Translations:Wikifunctions:Status updates/2026-06-05/24/de
1198
85270
281467
2026-06-07T19:02:59Z
Ameisenigel
44
Created page with "[[$1|ein Test der Standardfunktion]], der in Arabisch testet, einer von rechts nach links geschriebenen Sprache, und erfolgreich $2 mit einem Link generiert."
281467
wikitext
text/x-wiki
[[$1|ein Test der Standardfunktion]], der in Arabisch testet, einer von rechts nach links geschriebenen Sprache, und erfolgreich $2 mit einem Link generiert.
1q7spnm9cv1nt436b9384bbieq5m0aw
Translations:Wikifunctions:Status updates/2026-06-05/25/de
1198
85271
281469
2026-06-07T19:12:13Z
Ameisenigel
44
Created page with "Ich habe dieses Beispiel als Funktion der Woche ausgewählt, um zu weiterer Sprachkonfiguration zu ermutigen. Die Unterstützung einfacher Links mit Bezeichnungen nach einem einheitlichen Schema ist ein sehr klar definierter Anwendungszweck und stellt im Vergleich zu anderen Funktionen der natürlichen Sprache einen relativ einfachen Einstiegspunkt dar. Derzeit ist die Funktion nur für Englisch und Niederländisch [[$1|konfiguriert]], daher wären weitere Beiträge will..."
281469
wikitext
text/x-wiki
Ich habe dieses Beispiel als Funktion der Woche ausgewählt, um zu weiterer Sprachkonfiguration zu ermutigen. Die Unterstützung einfacher Links mit Bezeichnungen nach einem einheitlichen Schema ist ein sehr klar definierter Anwendungszweck und stellt im Vergleich zu anderen Funktionen der natürlichen Sprache einen relativ einfachen Einstiegspunkt dar. Derzeit ist die Funktion nur für Englisch und Niederländisch [[$1|konfiguriert]], daher wären weitere Beiträge willkommen. Zudem wäre es interessant, theoretische Ansätze für das aktuell ungenutzte Argument zur Konfiguration von Bezeichnungen zu diskutieren.
6tq2i9sicm8phsr72c8ivqxr3xpm85g
Translations:Wikifunctions:Status updates/2026-06-05/26/de
1198
85272
281471
2026-06-07T19:12:21Z
Ameisenigel
44
Created page with "=== Wöchentliche neue Funktionen: 63 neue Funktionen ==="
281471
wikitext
text/x-wiki
=== Wöchentliche neue Funktionen: 63 neue Funktionen ===
bubgy65grfg33v5glkrgt0uyo1gh3ci
Translations:Wikifunctions:Status updates/2026-06-05/31/de
1198
85273
281473
2026-06-07T19:12:28Z
Ameisenigel
44
Created page with "Diese Woche hatten wir 63 neue Funktionen. Hier ist eine unvollständige Liste von Funktionen mit Implementierungen und bestandenen Tests, um einen Eindruck davon zu bekommen, welche Funktionen erstellt wurden. Vielen Dank an alle für ihre Beiträge!"
281473
wikitext
text/x-wiki
Diese Woche hatten wir 63 neue Funktionen. Hier ist eine unvollständige Liste von Funktionen mit Implementierungen und bestandenen Tests, um einen Eindruck davon zu bekommen, welche Funktionen erstellt wurden. Vielen Dank an alle für ihre Beiträge!
myk67c0mwsiaeevj8sn295vjh0kk5jv
Translations:Wikifunctions:Status updates/2026-06-05/27/de
1198
85274
281475
2026-06-07T19:12:48Z
Ameisenigel
44
Created page with "Eine [$1 vollständige Liste aller Funktionen, sortiert nach ihrem Erstellungszeitpunkt], ist verfügbar."
281475
wikitext
text/x-wiki
Eine [$1 vollständige Liste aller Funktionen, sortiert nach ihrem Erstellungszeitpunkt], ist verfügbar.
iqqd8xtxnaqavmvf4afdevp8w7p7bfc
Module:Pagetype
828
85275
281484
2013-10-23T23:47:13Z
en>Mr. Stradivarius
0
print out the namespaces table
281484
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local p = {}
local defaultPagetypes = {
}
function p._main2(args)
local module = yesno(args.module)
if module == true then
module = 'module'
end
end
function p.main()
return dts(mw.site.namespaces)
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
ibe7knd3faky75z6ytryqp2ixlkjbsf
281485
281484
2013-10-23T23:48:17Z
en>Mr. Stradivarius
0
oops - try the right name this time
281485
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local p = {}
local defaultPagetypes = {
}
function p._main2(args)
local module = yesno(args.module)
if module == true then
module = 'module'
end
end
function p._main()
return dts(mw.site.namespaces)
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
ebvgs8ax99e9aa061gbl9h0x96fqczh
281486
281485
2013-10-24T01:33:35Z
en>Mr. Stradivarius
0
start a pagetype module alternative
281486
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local p = {}
local pagetypes = {
main = 'article',
mediawiki = 'interface page',
help = 'help page',
project = 'project page',
book = 'book',
file = 'file',
template = 'template',
category = 'category',
module = 'module',
portal = 'portal',
timedtext = 'Timed Text page',
['education program'] = 'education program page',
talk = 'talk page'
}
local function getNamespacePagetype(k, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[k]
end
if not ret then
ret = 'page'
end
return ret
end
function p._main(args)
return getNamespacePagetype('module', false)
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
9k63qeyubakh8iktr8zf31sbb2hj85f
281487
281486
2013-10-24T01:51:38Z
en>Mr. Stradivarius
0
allow custom values and process namespaces using [[Module:Namespace detect]]
281487
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetect = require('Module:Namespace detect')._main
local p = {}
local pagetypes = {
main = 'article',
mediawiki = 'interface page',
help = 'help page',
project = 'project page',
book = 'book',
file = 'file',
template = 'template',
category = 'category',
module = 'module',
portal = 'portal',
timedtext = 'Timed Text page',
['education program'] = 'education program page',
talk = 'talk page'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
function p._main(args)
local nsArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
nsArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments.
for namespace in pairs(pagetypes) do
local nsArg = getNamespacePagetype(namespace, args[namespace])
if nsArg then
nsArgs[namespace] = nsArg
end
end
-- Add the fallback value.
nsArgs.other = 'page'
-- Allow custom page and namespace values.
nsArgs.page = args.page
nsArgs.demospace = args.demospace
return nsDetect(nsArgs)
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
3uof5wofj0whgemk38d3g41p6scuidv
281488
281487
2013-10-24T03:07:45Z
en>Mr. Stradivarius
0
allow disabling of the default namespaces
281488
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetect = require('Module:Namespace detect')._main
local p = {}
local pagetypes = {
main = 'article',
mediawiki = 'interface page',
help = 'help page',
project = 'project page',
book = 'book',
file = 'file',
template = 'template',
category = 'category',
module = 'module',
portal = 'portal',
timedtext = 'Timed Text page',
['education program'] = 'education program page',
talk = 'talk page'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
function p._main(args)
local nsArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
nsArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments.
for namespace in pairs(pagetypes) do
local nsArg = getNamespacePagetype(namespace, args[namespace])
if nsArg ~= nil then
nsArgs[namespace] = nsArg
end
end
-- Add the fallback value.
nsArgs.other = 'page'
-- Allow custom page and namespace values.
nsArgs.page = args.page
nsArgs.demospace = args.demospace
return nsDetect(nsArgs)
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
s5v128rc9o5w7d7v890z0k0ji3dman0
281489
281488
2013-10-24T06:12:36Z
en>Mr. Stradivarius
0
use subject namespace for talk pages if no talk value is specified
281489
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetect = require('Module:Namespace detect')._main
local p = {}
local pagetypes = {
main = 'article',
mediawiki = 'interface page',
help = 'help page',
project = 'project page',
book = 'book',
file = 'file',
template = 'template',
category = 'category',
module = 'module',
portal = 'portal',
timedtext = 'Timed Text page',
['education program'] = 'education program page',
talk = 'talk page'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
function p._main(args)
local nsArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
nsArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments.
for namespace in pairs(pagetypes) do
local nsArg = getNamespacePagetype(namespace, args[namespace])
if nsArg ~= nil then
nsArgs[namespace] = nsArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not nsArgs.talk then
nsArgs.subjectns = true
end
-- Add the fallback value.
nsArgs.other = 'page'
-- Allow custom page and namespace values.
nsArgs.page = args.page
nsArgs.demospace = args.demospace
return nsDetect(nsArgs)
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
k2ig5m9xiv93yqiplzgjtonz7xneh81
281490
281489
2013-10-24T09:37:28Z
en>Mr. Stradivarius
0
add some missing namespaces
281490
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetect = require('Module:Namespace detect')._main
local p = {}
local pagetypes = {
main = 'article',
user = 'user page',
project = 'project page',
wikipedia = 'project page',
file = 'file',
mediawiki = 'interface page',
template = 'template',
help = 'help page',
category = 'category',
portal = 'portal',
book = 'book',
['education program'] = 'education program page',
timedtext = 'Timed Text page',
module = 'module',
talk = 'talk page',
special = 'special page',
media = 'file'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
function p._main(args)
local nsArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
nsArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments.
for namespace in pairs(pagetypes) do
local nsArg = getNamespacePagetype(namespace, args[namespace])
if nsArg ~= nil then
nsArgs[namespace] = nsArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not nsArgs.talk then
nsArgs.subjectns = true
end
-- Add the fallback value.
nsArgs.other = 'page'
-- Allow custom page and namespace values.
nsArgs.page = args.page
nsArgs.demospace = args.demospace
return nsDetect(nsArgs)
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
i4anwr2od2ns2b6qghyuv6q6erm76ym
281491
281490
2013-10-24T13:33:23Z
en>Mr. Stradivarius
0
automatically detect redirects
281491
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local pagetypes = {
main = 'article',
user = 'user page',
project = 'project page',
wikipedia = 'project page',
file = 'file',
mediawiki = 'interface page',
template = 'template',
help = 'help page',
category = 'category',
portal = 'portal',
book = 'book',
['education program'] = 'education program page',
timedtext = 'Timed Text page',
module = 'module',
talk = 'talk page',
special = 'special page',
media = 'file'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getNsDetectValue(args)
local nsArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
nsArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments.
for namespace in pairs(pagetypes) do
local nsArg = getNamespacePagetype(namespace, args[namespace])
if nsArg ~= nil then
nsArgs[namespace] = nsArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not nsArgs.talk then
nsArgs.subjectns = true
end
-- Add the fallback value.
nsArgs.other = 'page'
-- Allow custom page and namespace values.
nsArgs.page = args.page
return nsDetect(nsArgs)
end
function p._main(args)
local redirect = args.redirect
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect ~= false then
local pageObject = getPageObject(args.page)
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return 'redirect'
end
end
end
return getNsDetectValue(args)
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
j2i1ncyzm3upsir0zelqkj9v1ry58ej
281492
281491
2013-10-24T13:43:00Z
en>Mr. Stradivarius
0
should be ndArgs, not nsArgs
281492
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local pagetypes = {
main = 'article',
user = 'user page',
project = 'project page',
wikipedia = 'project page',
file = 'file',
mediawiki = 'interface page',
template = 'template',
help = 'help page',
category = 'category',
portal = 'portal',
book = 'book',
['education program'] = 'education program page',
timedtext = 'Timed Text page',
module = 'module',
talk = 'talk page',
special = 'special page',
media = 'file'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getNsDetectValue(args)
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments.
for namespace in pairs(pagetypes) do
local ndArg = getNamespacePagetype(namespace, args[namespace])
if ndArg ~= nil then
ndArgs[namespace] = ndArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = 'page'
-- Allow custom page and namespace values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
function p._main(args)
local redirect = args.redirect
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect ~= false then
local pageObject = getPageObject(args.page)
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return 'redirect'
end
end
end
return getNsDetectValue(args)
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
pyhb5fp6urw52cp44o2bwykwm1leuf0
281493
281492
2013-10-24T14:41:56Z
en>Mr. Stradivarius
0
use the subject page for redirect testing if no talk argument is present
281493
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local pagetypes = {
main = 'article',
user = 'user page',
project = 'project page',
wikipedia = 'project page',
file = 'file',
mediawiki = 'interface page',
template = 'template',
help = 'help page',
category = 'category',
portal = 'portal',
book = 'book',
['education program'] = 'education program page',
timedtext = 'Timed Text page',
module = 'module',
talk = 'talk page',
special = 'special page',
media = 'file'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getNsDetectValue(args)
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments.
for namespace in pairs(pagetypes) do
local ndArg = getNamespacePagetype(namespace, args[namespace])
if ndArg ~= nil then
ndArgs[namespace] = ndArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = 'page'
-- Allow custom page and namespace values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args.redirect
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return 'redirect'
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
k13jpiln7l8l9jg8li5zdcr7qwxsgi6
281494
281493
2013-10-25T06:05:04Z
en>Mr. Stradivarius
0
convert false values to nil to protect against possible changes to [[Module:Namespace detect]]
281494
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local pagetypes = {
main = 'article',
user = 'user page',
project = 'project page',
wikipedia = 'project page',
file = 'file',
mediawiki = 'interface page',
template = 'template',
help = 'help page',
category = 'category',
portal = 'portal',
book = 'book',
['education program'] = 'education program page',
timedtext = 'Timed Text page',
module = 'module',
talk = 'talk page',
special = 'special page',
media = 'file'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getNsDetectValue(args)
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments.
for namespace in pairs(pagetypes) do
local ndArg = getNamespacePagetype(namespace, args[namespace])
if ndArg ~= nil then
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
else
ndArgs[namespace] = ndArg
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = 'page'
-- Allow custom page and namespace values.
ndArgs.page = args.page
for k, v in pairs(ndArgs) do
end
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args.redirect
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return 'redirect'
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
o141i2g1y44ywgo05t1etcykifu9993
281495
281494
2013-10-25T06:14:24Z
en>Mr. Stradivarius
0
slightly cleaner logic
281495
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local pagetypes = {
main = 'article',
user = 'user page',
project = 'project page',
wikipedia = 'project page',
file = 'file',
mediawiki = 'interface page',
template = 'template',
help = 'help page',
category = 'category',
portal = 'portal',
book = 'book',
['education program'] = 'education program page',
timedtext = 'Timed Text page',
module = 'module',
talk = 'talk page',
special = 'special page',
media = 'file'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getNsDetectValue(args)
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments.
for namespace in pairs(pagetypes) do
local ndArg = getNamespacePagetype(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = 'page'
-- Allow custom page and namespace values.
ndArgs.page = args.page
for k, v in pairs(ndArgs) do
end
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args.redirect
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return 'redirect'
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
0cuqi2xyj6smnq2etdqp88d69pneio1
281496
281495
2013-10-25T06:16:05Z
en>Mr. Stradivarius
0
comment tweak
281496
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local pagetypes = {
main = 'article',
user = 'user page',
project = 'project page',
wikipedia = 'project page',
file = 'file',
mediawiki = 'interface page',
template = 'template',
help = 'help page',
category = 'category',
portal = 'portal',
book = 'book',
['education program'] = 'education program page',
timedtext = 'Timed Text page',
module = 'module',
talk = 'talk page',
special = 'special page',
media = 'file'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getNsDetectValue(args)
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(pagetypes) do
local ndArg = getNamespacePagetype(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = 'page'
-- Allow custom page and namespace values.
ndArgs.page = args.page
for k, v in pairs(ndArgs) do
end
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args.redirect
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return 'redirect'
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
c1e7v5e5hzyapmx1d8lkh43rbbwstp2
281497
281496
2013-10-25T06:17:10Z
en>Mr. Stradivarius
0
remove unused for loop I had forgotten about, and tweak another comment
281497
Scribunto
text/plain
local dts = require('Module:User:Anomie/deepToString').deepToString -- for debugging
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local pagetypes = {
main = 'article',
user = 'user page',
project = 'project page',
wikipedia = 'project page',
file = 'file',
mediawiki = 'interface page',
template = 'template',
help = 'help page',
category = 'category',
portal = 'portal',
book = 'book',
['education program'] = 'education program page',
timedtext = 'Timed Text page',
module = 'module',
talk = 'talk page',
special = 'special page',
media = 'file'
}
local defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getNsDetectValue(args)
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(pagetypes) do
local ndArg = getNamespacePagetype(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = 'page'
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args.redirect
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return 'redirect'
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
m0dcgfccq4vxdaat29m47c4rspws3jv
281498
281497
2013-10-25T06:37:13Z
en>Mr. Stradivarius
0
use a configuration table for easy porting to other wikis
281498
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['file'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The value used if the module matches a namespace that has not been specified.
cfg.fallback = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
-- Move some of the cfg tables to local variables.
local pagetypes = cfg.pagetypes
local defaultNamespaces = cfg.defaultNamespaces
local p = {}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getNsDetectValue(args)
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(pagetypes) do
local ndArg = getNamespacePagetype(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = cfg.fallback
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args.redirect
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return 'redirect'
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
5hs4cufy5du1fcmh7p4w4o94fwpsrhn
281499
281498
2013-10-25T06:39:34Z
en>Mr. Stradivarius
0
move the default value for redirects to the config table
281499
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['file'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The default value to use for redirects.
cfg.redirect = 'redirect'
-- The value used if the module matches a namespace that has not been specified.
cfg.fallback = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
-- Move some of the cfg tables to local variables.
local pagetypes = cfg.pagetypes
local defaultNamespaces = cfg.defaultNamespaces
local p = {}
local function getNamespacePagetype(namespace, v)
local ret = yesno(v, v) -- Returns true/false for "yes", "no", etc., and returns v for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getNsDetectValue(args)
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(pagetypes) do
local ndArg = getNamespacePagetype(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = cfg.fallback
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args.redirect
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirect
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
a70se4zh9mpezxrvkfm3lbeyh9pxra9
281500
281499
2013-10-25T07:03:43Z
en>Mr. Stradivarius
0
add some cfg values to be used, and improve comments
281500
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['file'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- This table holds the different possible aliases for disambiguation-class pages.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dab = 'page'
-- This table holds the different possible aliases for N/A-class pages.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.na = 'page'
-- The default value to use for redirects.
cfg.redirect = 'redirect'
-- The value used if the module matches a namespace that has not been specified.
cfg.fallback = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
-- Move some of the cfg tables to local variables.
local pagetypes = cfg.pagetypes
local defaultNamespaces = cfg.defaultNamespaces
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(pagetypes) do
local ndArg = checkPagetypeInput(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = cfg.fallback
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args.redirect
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirect
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
p0qbmbxaol52hzkvxthm9r0kqa0cghi
281501
281500
2013-10-25T07:38:05Z
en>Mr. Stradivarius
0
add detection for disambig and na classes from the first positional parameter
281501
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['file'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The value used if the module matches a namespace that has not been specified.
cfg.fallback = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
-- Move some of the cfg tables to local variables.
local pagetypes = cfg.pagetypes
local defaultNamespaces = cfg.defaultNamespaces
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(pagetypes) do
local ndArg = checkPagetypeInput(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If the main namespace argument is present, check for disambiguation-class and N/A-class pages.
if ndArgs.main then
local class = args[1]
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = cfg.fallback
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
j3u9gxiwavbv0gcigz5hovhdfsy79wl
281502
281501
2013-10-25T09:27:48Z
en>Mr. Stradivarius
0
Mr. Stradivarius moved page [[Module:User:Mr. Stradivarius/sandbox4]] to [[Module:Pagetype]] without leaving a redirect: ready to be moved to its proper title
281501
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['file'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The value used if the module matches a namespace that has not been specified.
cfg.fallback = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
-- Move some of the cfg tables to local variables.
local pagetypes = cfg.pagetypes
local defaultNamespaces = cfg.defaultNamespaces
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(defaultNamespaces) do
ndArgs[namespace] = pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(pagetypes) do
local ndArg = checkPagetypeInput(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If the main namespace argument is present, check for disambiguation-class and N/A-class pages.
if ndArgs.main then
local class = args[1]
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = cfg.fallback
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
j3u9gxiwavbv0gcigz5hovhdfsy79wl
281503
281502
2013-10-25T09:48:09Z
en>Mr. Stradivarius
0
don't convert cfg table values into local parameters
281503
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['file'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The value used if the module matches a namespace that has not been specified.
cfg.fallback = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(cfg.defaultNamespaces) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(cfg.pagetypes) do
local ndArg = checkPagetypeInput(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If the main namespace argument is present, check for disambiguation-class and N/A-class pages.
if ndArgs.main then
local class = args[1]
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = cfg.fallback
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
2pkv7rpa5drrycqtkdi2d48yokhdtbv
281504
281503
2013-10-25T09:51:18Z
en>Mr. Stradivarius
0
add a comment about the redirects
281504
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['file'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The value used if the module matches a namespace that has not been specified.
cfg.fallback = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(cfg.defaultNamespaces) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(cfg.pagetypes) do
local ndArg = checkPagetypeInput(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If the main namespace argument is present, check for disambiguation-class and N/A-class pages.
if ndArgs.main then
local class = args[1]
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value.
ndArgs.other = cfg.fallback
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
6pdszago5g7unbmo45ye105tc6acsyv
281505
281504
2013-10-25T10:19:52Z
en>Mr. Stradivarius
0
allow customisation of the fallback value as well
281505
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['file'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(cfg.defaultNamespaces) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(cfg.pagetypes) do
local ndArg = checkPagetypeInput(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If the main namespace argument is present, check for disambiguation-class and N/A-class pages.
if ndArgs.main then
local class = args[1]
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
g1a883zgihlfyv3ab2vvhnq77aaz4mp
281506
281505
2013-10-25T10:30:03Z
en>Mr. Stradivarius
0
add "wp"
281506
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(cfg.defaultNamespaces) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(cfg.pagetypes) do
local ndArg = checkPagetypeInput(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If the main namespace argument is present, check for disambiguation-class and N/A-class pages.
if ndArgs.main then
local class = args[1]
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
lgon2zxqi37xkxkr4rp0ce0r7kvkme6
281507
281506
2013-10-25T10:35:50Z
en>Mr. Stradivarius
0
add the image namespace too
281507
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(cfg.defaultNamespaces) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(cfg.pagetypes) do
local ndArg = checkPagetypeInput(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If the main namespace argument is present, check for disambiguation-class and N/A-class pages.
if ndArgs.main then
local class = args[1]
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
60mfr3ptdj15rv6k486j7wts7cb7ti4
281508
281507
2013-10-25T10:44:15Z
en>Mr. Stradivarius
0
use yesno with "other" for consistency
281508
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- Language-specific parameter names can be set here. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(cfg.defaultNamespaces) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(cfg.pagetypes) do
local ndArg = checkPagetypeInput(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If the main namespace argument is present, check for disambiguation-class and N/A-class pages.
if ndArgs.main then
local class = args[1]
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
other = yesno(other, other) -- We will ignore true/false/nil results from yesno here, but using it anyway for consistency.
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
pkxf3h833ttlz6taefv8s5vdkz51cad
281509
281508
2013-10-25T11:01:31Z
en>Mr. Stradivarius
0
put the class value in lower case to match both "Dab" and "dab" etc., and some more comment tweaking
281509
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- This module can be localised for other wikis by using the configuration parameters below. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages. These should be lower-case.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(cfg.defaultNamespaces) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
for namespace in pairs(cfg.pagetypes) do
local ndArg = checkPagetypeInput(namespace, args[namespace])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[namespace] = nil
elseif ndArg then
ndArgs[namespace] = ndArg
end
end
-- If the main namespace argument is present, check for disambiguation-class and N/A-class pages.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
other = yesno(other, other) -- We will ignore true/false/nil results from yesno here, but using it anyway for consistency.
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
8g8paxy036a82lg00mlx9mh06bgkrwx
281510
281509
2013-10-25T11:28:45Z
en>Mr. Stradivarius
0
get the namespace parameter names from [[Module:Namespace detect]] automatically in case any new namespaces are added
281510
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- This module can be localised for other wikis by using the configuration parameters below. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages. These should be lower-case.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getParamMappings = nsDetectModule.getParamMappings
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(cfg.defaultNamespaces) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
-- The possible argument names are fetched from [[Module:Namespace detect]] automatically in case new namespaces are added.
local mappings = getParamMappings()
for ns, paramNames in pairs(mappings) do
for _, paramName in ipairs(paramNames) do
local ndArg = checkPagetypeInput(paramName, args[paramName])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[paramName] = nil
elseif ndArg then
ndArgs[paramName] = ndArg
end
end
end
-- Check for disambiguation-class and N/A-class pages in mainspace.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
other = yesno(other, other) -- We will ignore true/false/nil results from yesno here, but using it anyway for consistency.
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
ocziuzgbdcpvw961smrup4yfh38jozw
281511
281510
2013-10-25T14:07:49Z
en>Mr. Stradivarius
0
allow namespace alias parameters to overwrite default values
281511
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- This module can be localised for other wikis by using the configuration parameters below. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages. These should be lower-case.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getParamMappings = nsDetectModule.getParamMappings
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(cfg.defaultNamespaces) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
-- The possible argument names are fetched from [[Module:Namespace detect]] automatically in case new namespaces are added.
-- Although we accept namespace aliases as parameters, we only pass the local namespace name as a parameter to [[Module:Namespace detect]].
-- This means that the "image" parameter can overwrite defaults for the File: namespace, which wouldn't work if we passed the parameters through separately.
local mappings = getParamMappings()
for ns, paramAliases in pairs(mappings) do
local paramName = paramAliases[1]
for i = #paramAliases, 1, -1 do -- Iterate backwards along the array so that any values for the local namespace names overwrite those for namespace aliases.
local paramAlias = paramAliases[i]
local ndArg = checkPagetypeInput(paramAlias, args[paramAlias])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[paramName] = nil
elseif ndArg then
ndArgs[paramName] = ndArg
end
end
end
-- Check for disambiguation-class and N/A-class pages in mainspace.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
other = yesno(other, other) -- We will ignore true/false/nil results from yesno here, but using it anyway for consistency.
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
9wak9iqesqaqigzbvi6mztnlkqysqp1
281512
281511
2013-10-26T04:13:04Z
en>Mr. Stradivarius on tour
0
add an extended namespaces list
281512
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- This module can be localised for other wikis by using the configuration parameters below. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by the "default" function.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- This table holds the names of the namespaces to be looked up from cry.pagetypes by the "extended" function.
cfg.extendedNamespaces = {'main', 'user', 'project', 'file', 'mediawiki', 'template', 'category', 'help', 'portal', 'module', 'book'}
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages. These should be lower-case.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getParamMappings = nsDetectModule.getParamMappings
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
local ndArgs = {}
-- Get the default values.
for _, namespace in ipairs(cfg.defaultNamespaces) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
-- The possible argument names are fetched from [[Module:Namespace detect]] automatically in case new namespaces are added.
-- Although we accept namespace aliases as parameters, we only pass the local namespace name as a parameter to [[Module:Namespace detect]].
-- This means that the "image" parameter can overwrite defaults for the File: namespace, which wouldn't work if we passed the parameters through separately.
local mappings = getParamMappings()
for ns, paramAliases in pairs(mappings) do
local paramName = paramAliases[1]
for i = #paramAliases, 1, -1 do -- Iterate backwards along the array so that any values for the local namespace names overwrite those for namespace aliases.
local paramAlias = paramAliases[i]
local ndArg = checkPagetypeInput(paramAlias, args[paramAlias])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes
-- to [[Module:Namespace detect]].
ndArgs[paramName] = nil
elseif ndArg then
ndArgs[paramName] = ndArg
end
end
end
-- Check for disambiguation-class and N/A-class pages in mainspace.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
other = yesno(other, other) -- We will ignore true/false/nil results from yesno here, but using it anyway for consistency.
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
bj630a60rm1eo1cb8mfr3ljki7yhzjs
281513
281512
2013-10-26T08:00:06Z
en>Mr. Stradivarius
0
add a defaultns parameter to allow different sets of default namespaces
281513
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- This module can be localised for other wikis by using the configuration parameters below. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- This table holds the names of the namespaces to be looked up from cry.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {'main', 'user', 'project', 'file', 'mediawiki', 'template', 'category', 'help', 'portal', 'module', 'book'}
-- The parameter name to set which default namespace values to be looked up from cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages. These should be lower-case.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getParamMappings = nsDetectModule.getParamMappings
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
-- First, get the default values.
local ndArgs = {}
local defaultns = args[cfg.defaultns]
if defaultns == cfg.defaultnsAll then
ndArgs = cfg.pagetypes
else
local defaultnsArray
if defaultns == cfg.defaultnsExtended then
defaultnsArray = cfg.extendedNamespaces
elseif defaultns == cfg.defaultnsNone then
defaultnsArray = {}
else
defaultnsArray = cfg.defaultNamespaces
end
for _, namespace in ipairs(defaultnsArray) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
-- The possible argument names are fetched from [[Module:Namespace detect]] automatically in case new namespaces are added.
-- Although we accept namespace aliases as parameters, we only pass the local namespace name as a parameter to [[Module:Namespace detect]].
-- This means that the "image" parameter can overwrite defaults for the File: namespace, which wouldn't work if we passed the parameters through separately.
local mappings = getParamMappings()
for ns, paramAliases in pairs(mappings) do
local paramName = paramAliases[1]
for i = #paramAliases, 1, -1 do -- Iterate backwards along the array so that any values for the local namespace names overwrite those for namespace aliases.
local paramAlias = paramAliases[i]
local ndArg = checkPagetypeInput(paramAlias, args[paramAlias])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes to [[Module:Namespace detect]].
ndArgs[paramName] = nil
elseif ndArg then
ndArgs[paramName] = ndArg
end
end
end
-- Check for disambiguation-class and N/A-class pages in mainspace.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
other = yesno(other, other) -- We will ignore true/false/nil results from yesno here, but using it anyway for consistency.
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
dhx45goteba1xj4jymnfb78ku625r82
281514
281513
2013-11-09T05:52:06Z
en>Mr. Stradivarius
0
Protected Module:Pagetype: [[Wikipedia:Lua/Modules|High-risk Lua module]]: ~5,300,000 transclusions ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite))
281513
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table.
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- This module can be localised for other wikis by using the configuration parameters below. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- This table holds the names of the namespaces to be looked up from cry.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {'main', 'user', 'project', 'file', 'mediawiki', 'template', 'category', 'help', 'portal', 'module', 'book'}
-- The parameter name to set which default namespace values to be looked up from cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages. These should be lower-case.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getParamMappings = nsDetectModule.getParamMappings
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
-- First, get the default values.
local ndArgs = {}
local defaultns = args[cfg.defaultns]
if defaultns == cfg.defaultnsAll then
ndArgs = cfg.pagetypes
else
local defaultnsArray
if defaultns == cfg.defaultnsExtended then
defaultnsArray = cfg.extendedNamespaces
elseif defaultns == cfg.defaultnsNone then
defaultnsArray = {}
else
defaultnsArray = cfg.defaultNamespaces
end
for _, namespace in ipairs(defaultnsArray) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
-- The possible argument names are fetched from [[Module:Namespace detect]] automatically in case new namespaces are added.
-- Although we accept namespace aliases as parameters, we only pass the local namespace name as a parameter to [[Module:Namespace detect]].
-- This means that the "image" parameter can overwrite defaults for the File: namespace, which wouldn't work if we passed the parameters through separately.
local mappings = getParamMappings()
for ns, paramAliases in pairs(mappings) do
local paramName = paramAliases[1]
for i = #paramAliases, 1, -1 do -- Iterate backwards along the array so that any values for the local namespace names overwrite those for namespace aliases.
local paramAlias = paramAliases[i]
local ndArg = checkPagetypeInput(paramAlias, args[paramAlias])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes to [[Module:Namespace detect]].
ndArgs[paramName] = nil
elseif ndArg then
ndArgs[paramName] = ndArg
end
end
end
-- Check for disambiguation-class and N/A-class pages in mainspace.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
other = yesno(other, other) -- We will ignore true/false/nil results from yesno here, but using it anyway for consistency.
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Trim whitespace and remove blank arguments.
local args = {}
for k, v in pairs(origArgs) do
if type(v) == 'string' then
v = mw.text.trim(v)
end
if v ~= '' then
args[k] = v
end
end
return p._main(args)
end
return p
dhx45goteba1xj4jymnfb78ku625r82
281515
281514
2013-12-27T12:20:32Z
en>Mr. Stradivarius
0
add draft namespace, use [[Module:Arguments]] to get the arguments, and make a beautification fix to the top comment
281515
Scribunto
text/plain
----------------------------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar templates. It --
-- automatically detects namespaces, and allows for a great deal of customisation. --
-- It can easily be ported to other wikis by changing the values in the configuration --
-- table. --
-- --
----------------------------------------------------------------------------------------------------
local cfg = {}
----------------------------------------------------------------------------------------------------
-- Configuration data --
-- This module can be localised for other wikis by using the configuration parameters below. --
----------------------------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to this table should
-- be namespace parameters that can be used with [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['draft'] = 'draft',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from cfg.pagetypes by default.
cfg.defaultNamespaces = {'main', 'file', 'template', 'category', 'module', 'book'}
-- This table holds the names of the namespaces to be looked up from cry.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {'main', 'user', 'project', 'file', 'mediawiki', 'template', 'category', 'help', 'portal', 'module', 'book', 'draft'}
-- The parameter name to set which default namespace values to be looked up from cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class pages. These should be lower-case.
cfg.dabAliases = {'disambiguation', 'disambig', 'disamb', 'dab'}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
----------------------------------------------------------------------------------------------------
-- End configuration data --
----------------------------------------------------------------------------------------------------
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getParamMappings = nsDetectModule.getParamMappings
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace, and if so gets it from the pagetypes table.
local ret = yesno(val, val) -- Returns true/false for "yes", "no", etc., and returns val for other input.
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional parameter.
param = yesno(param, param)
if param ~= false then -- Check for classes unless they are specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns the result.
-- First, get the default values.
local ndArgs = {}
local defaultns = args[cfg.defaultns]
if defaultns == cfg.defaultnsAll then
ndArgs = cfg.pagetypes
else
local defaultnsArray
if defaultns == cfg.defaultnsExtended then
defaultnsArray = cfg.extendedNamespaces
elseif defaultns == cfg.defaultnsNone then
defaultnsArray = {}
else
defaultnsArray = cfg.defaultNamespaces
end
for _, namespace in ipairs(defaultnsArray) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
end
-- Add custom values passed in from the arguments. These overwrite the defaults.
-- The possible argument names are fetched from [[Module:Namespace detect]] automatically in case new namespaces are added.
-- Although we accept namespace aliases as parameters, we only pass the local namespace name as a parameter to [[Module:Namespace detect]].
-- This means that the "image" parameter can overwrite defaults for the File: namespace, which wouldn't work if we passed the parameters through separately.
local mappings = getParamMappings()
for ns, paramAliases in pairs(mappings) do
local paramName = paramAliases[1]
for i = #paramAliases, 1, -1 do -- Iterate backwards along the array so that any values for the local namespace names overwrite those for namespace aliases.
local paramAlias = paramAliases[i]
local ndArg = checkPagetypeInput(paramAlias, args[paramAlias])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect against breakage by future changes to [[Module:Namespace detect]].
ndArgs[paramName] = nil
elseif ndArg then
ndArgs[paramName] = ndArg
end
end
end
-- Check for disambiguation-class and N/A-class pages in mainspace.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(class, args[cfg.dab], cfg.dabAliases, cfg.dabDefault)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(class, args[cfg.na], cfg.naAliases, cfg.naDefault)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be disabled.
local other = args[cfg.other]
other = yesno(other, other) -- We will ignore true/false/nil results from yesno here, but using it anyway for consistency.
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
redirect = yesno(redirect, redirect) -- Returns true/false for "yes", "no", etc., and returns redirect for other input.
if redirect == false then return end -- Detect redirects unless they have been explicitly disallowed with "redirect=no" or similar.
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject and not yesno(args.talk, true) then
pageObject = getPageObject(pageObject.subjectNsText .. ':' .. pageObject.text)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
emnmdch9bz8vv07ok2tnxfgjgat0vw0
281516
281515
2014-04-04T05:55:12Z
en>Mr. Stradivarius
0
Bug fixes - make page object detection respect defaultns with redirects, and fix mappings table handling after switch to mw.loadData. Also, move config data to [[Module:Pagetype/config]], plus beautification tweaks.
281516
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar --
-- templates. It automatically detects namespaces, and allows for a --
-- great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getParamMappings = nsDetectModule.getParamMappings
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function shallowCopy(t)
-- Makes a shallow copy of a table.
local ret = {}
for k, v in pairs(t) do
ret[k] = v
end
return ret
end
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace,
-- and if so gets it from the pagetypes table.
-- The yesno function returns true/false for "yes", "no", etc., and returns
-- val for other input.
local ret = yesno(val, val)
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional
-- parameter.
param = yesno(param, param)
if param ~= false then -- No check if specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns
-- the result.
-- Get the default values.
local ndArgs = {}
local defaultns = args[cfg.defaultns]
if defaultns == cfg.defaultnsAll then
ndArgs = shallowCopy(cfg.pagetypes)
else
local defaultnsArray
if defaultns == cfg.defaultnsExtended then
defaultnsArray = cfg.extendedNamespaces
elseif defaultns == cfg.defaultnsNone then
defaultnsArray = {}
else
defaultnsArray = cfg.defaultNamespaces
end
for _, namespace in ipairs(defaultnsArray) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
end
--[[
-- Add custom values passed in from the arguments. These overwrite the
-- defaults. The possible argument names are fetched from
-- Module:Namespace detect automatically in case new namespaces are
-- added. Although we accept namespace aliases as parameters, we only pass
-- the local namespace name as a parameter to Module:Namespace detect.
-- This means that the "image" parameter can overwrite defaults for the
-- File: namespace, which wouldn't work if we passed the parameters through
-- separately.
--]]
local mappings = getParamMappings()
for ns, paramAliases in pairs(mappings) do
-- Copy the aliases table, as # doesn't work with tables returned from
-- mw.loadData.
paramAliases = shallowCopy(paramAliases)
local paramName = paramAliases[1]
-- Iterate backwards along the array so that any values for the local
-- namespace names overwrite those for namespace aliases.
for i = #paramAliases, 1, -1 do
local paramAlias = paramAliases[i]
local ndArg = checkPagetypeInput(paramAlias, args[paramAlias])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect
-- against breakage by future changes to
-- [[Module:Namespace detect]].
ndArgs[paramName] = nil
elseif ndArg then
ndArgs[paramName] = ndArg
end
end
end
-- Check for disambiguation-class and N/A-class pages in mainspace.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(
class,
args[cfg.dab],
cfg.dabAliases,
cfg.dabDefault
)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(
class,
args[cfg.na],
cfg.naAliases,
cfg.naDefault
)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject
-- namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be
-- disabled.
local other = args[cfg.other]
-- We will ignore true/false/nil results from yesno here, but using it
-- anyway for consistency.
other = yesno(other, other)
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
-- The yesno function returns true/false for "yes", "no", etc., and returns
-- redirect for other input.
redirect = yesno(redirect, redirect)
if redirect == false then
-- Detect redirects unless they have been explicitly disallowed with
-- "redirect=no" or similar.
return
end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject
and not yesno(args.talk, true)
and args[cfg.defaultns] ~= cfg.defaultnsAll
then
pageObject = getPageObject(
pageObject.subjectNsText .. ':' .. pageObject.text
)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
95yj90zj7nf2doh38om166uuyivtaoq
281517
281516
2019-01-22T16:51:09Z
en>Galobtter
0
Changed protection level for "[[Module:Pagetype]]": Nearly 8 million transclusions, only template used in WPBannerMeta not to be fully protected ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite))
281516
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar --
-- templates. It automatically detects namespaces, and allows for a --
-- great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getParamMappings = nsDetectModule.getParamMappings
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function shallowCopy(t)
-- Makes a shallow copy of a table.
local ret = {}
for k, v in pairs(t) do
ret[k] = v
end
return ret
end
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace,
-- and if so gets it from the pagetypes table.
-- The yesno function returns true/false for "yes", "no", etc., and returns
-- val for other input.
local ret = yesno(val, val)
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional
-- parameter.
param = yesno(param, param)
if param ~= false then -- No check if specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns
-- the result.
-- Get the default values.
local ndArgs = {}
local defaultns = args[cfg.defaultns]
if defaultns == cfg.defaultnsAll then
ndArgs = shallowCopy(cfg.pagetypes)
else
local defaultnsArray
if defaultns == cfg.defaultnsExtended then
defaultnsArray = cfg.extendedNamespaces
elseif defaultns == cfg.defaultnsNone then
defaultnsArray = {}
else
defaultnsArray = cfg.defaultNamespaces
end
for _, namespace in ipairs(defaultnsArray) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
end
--[[
-- Add custom values passed in from the arguments. These overwrite the
-- defaults. The possible argument names are fetched from
-- Module:Namespace detect automatically in case new namespaces are
-- added. Although we accept namespace aliases as parameters, we only pass
-- the local namespace name as a parameter to Module:Namespace detect.
-- This means that the "image" parameter can overwrite defaults for the
-- File: namespace, which wouldn't work if we passed the parameters through
-- separately.
--]]
local mappings = getParamMappings()
for ns, paramAliases in pairs(mappings) do
-- Copy the aliases table, as # doesn't work with tables returned from
-- mw.loadData.
paramAliases = shallowCopy(paramAliases)
local paramName = paramAliases[1]
-- Iterate backwards along the array so that any values for the local
-- namespace names overwrite those for namespace aliases.
for i = #paramAliases, 1, -1 do
local paramAlias = paramAliases[i]
local ndArg = checkPagetypeInput(paramAlias, args[paramAlias])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect
-- against breakage by future changes to
-- [[Module:Namespace detect]].
ndArgs[paramName] = nil
elseif ndArg then
ndArgs[paramName] = ndArg
end
end
end
-- Check for disambiguation-class and N/A-class pages in mainspace.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(
class,
args[cfg.dab],
cfg.dabAliases,
cfg.dabDefault
)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(
class,
args[cfg.na],
cfg.naAliases,
cfg.naDefault
)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject
-- namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be
-- disabled.
local other = args[cfg.other]
-- We will ignore true/false/nil results from yesno here, but using it
-- anyway for consistency.
other = yesno(other, other)
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
-- The yesno function returns true/false for "yes", "no", etc., and returns
-- redirect for other input.
redirect = yesno(redirect, redirect)
if redirect == false then
-- Detect redirects unless they have been explicitly disallowed with
-- "redirect=no" or similar.
return
end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject
and not yesno(args.talk, true)
and args[cfg.defaultns] ~= cfg.defaultnsAll
then
pageObject = getPageObject(
pageObject.subjectNsText .. ':' .. pageObject.text
)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
if redirect then
return redirect
else
return getNsDetectValue(args)
end
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
95yj90zj7nf2doh38om166uuyivtaoq
281518
281517
2020-06-01T17:54:00Z
en>RexxS
0
update from sandbox - implement plurals
281518
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar --
-- templates. It automatically detects namespaces, and allows for a --
-- great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getParamMappings = nsDetectModule.getParamMappings
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function shallowCopy(t)
-- Makes a shallow copy of a table.
local ret = {}
for k, v in pairs(t) do
ret[k] = v
end
return ret
end
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace,
-- and if so gets it from the pagetypes table.
-- The yesno function returns true/false for "yes", "no", etc., and returns
-- val for other input.
local ret = yesno(val, val)
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional
-- parameter.
param = yesno(param, param)
if param ~= false then -- No check if specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns
-- the result.
-- Get the default values.
local ndArgs = {}
local defaultns = args[cfg.defaultns]
if defaultns == cfg.defaultnsAll then
ndArgs = shallowCopy(cfg.pagetypes)
else
local defaultnsArray
if defaultns == cfg.defaultnsExtended then
defaultnsArray = cfg.extendedNamespaces
elseif defaultns == cfg.defaultnsNone then
defaultnsArray = {}
else
defaultnsArray = cfg.defaultNamespaces
end
for _, namespace in ipairs(defaultnsArray) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
end
--[[
-- Add custom values passed in from the arguments. These overwrite the
-- defaults. The possible argument names are fetched from
-- Module:Namespace detect automatically in case new namespaces are
-- added. Although we accept namespace aliases as parameters, we only pass
-- the local namespace name as a parameter to Module:Namespace detect.
-- This means that the "image" parameter can overwrite defaults for the
-- File: namespace, which wouldn't work if we passed the parameters through
-- separately.
--]]
local mappings = getParamMappings()
for ns, paramAliases in pairs(mappings) do
-- Copy the aliases table, as # doesn't work with tables returned from
-- mw.loadData.
paramAliases = shallowCopy(paramAliases)
local paramName = paramAliases[1]
-- Iterate backwards along the array so that any values for the local
-- namespace names overwrite those for namespace aliases.
for i = #paramAliases, 1, -1 do
local paramAlias = paramAliases[i]
local ndArg = checkPagetypeInput(paramAlias, args[paramAlias])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect
-- against breakage by future changes to
-- [[Module:Namespace detect]].
ndArgs[paramName] = nil
elseif ndArg then
ndArgs[paramName] = ndArg
end
end
end
-- Check for disambiguation-class and N/A-class pages in mainspace.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(
class,
args[cfg.dab],
cfg.dabAliases,
cfg.dabDefault
)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(
class,
args[cfg.na],
cfg.naAliases,
cfg.naDefault
)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject
-- namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be
-- disabled.
local other = args[cfg.other]
-- We will ignore true/false/nil results from yesno here, but using it
-- anyway for consistency.
other = yesno(other, other)
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
-- The yesno function returns true/false for "yes", "no", etc., and returns
-- redirect for other input.
redirect = yesno(redirect, redirect)
if redirect == false then
-- Detect redirects unless they have been explicitly disallowed with
-- "redirect=no" or similar.
return
end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject
and not yesno(args.talk, true)
and args[cfg.defaultns] ~= cfg.defaultnsAll
then
pageObject = getPageObject(
pageObject.subjectNsText .. ':' .. pageObject.text
)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
local pagetype = ""
if redirect then
pagetype = redirect
else
pagetype = getNsDetectValue(args)
end
if yesno(args.plural, false) then
if cfg.irregularPlurals[pagetype] then
pagetype = cfg.irregularPlurals[pagetype]
else
pagetype = pagetype .. cfg.plural -- often 's'
end
end
return pagetype
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
pffu9pmd5dx7kb2q672d9txyum4v9bx
281519
281518
2020-06-18T21:22:08Z
en>RexxS
0
add caps parameter per talk request
281519
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar --
-- templates. It automatically detects namespaces, and allows for a --
-- great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local nsDetectModule = require('Module:Namespace detect')
local nsDetect = nsDetectModule._main
local getParamMappings = nsDetectModule.getParamMappings
local getPageObject = nsDetectModule.getPageObject
local p = {}
local function shallowCopy(t)
-- Makes a shallow copy of a table.
local ret = {}
for k, v in pairs(t) do
ret[k] = v
end
return ret
end
local function checkPagetypeInput(namespace, val)
-- Checks to see whether we need the default value for the given namespace,
-- and if so gets it from the pagetypes table.
-- The yesno function returns true/false for "yes", "no", etc., and returns
-- val for other input.
local ret = yesno(val, val)
if ret and type(ret) ~= 'string' then
ret = cfg.pagetypes[namespace]
end
return ret
end
local function getPagetypeFromClass(class, param, aliasTable, default)
-- Gets the pagetype from a class specified from the first positional
-- parameter.
param = yesno(param, param)
if param ~= false then -- No check if specifically disallowed.
for _, alias in ipairs(aliasTable) do
if class == alias then
if type(param) == 'string' then
return param
else
return default
end
end
end
end
end
local function getNsDetectValue(args)
-- Builds the arguments to pass to [[Module:Namespace detect]] and returns
-- the result.
-- Get the default values.
local ndArgs = {}
local defaultns = args[cfg.defaultns]
if defaultns == cfg.defaultnsAll then
ndArgs = shallowCopy(cfg.pagetypes)
else
local defaultnsArray
if defaultns == cfg.defaultnsExtended then
defaultnsArray = cfg.extendedNamespaces
elseif defaultns == cfg.defaultnsNone then
defaultnsArray = {}
else
defaultnsArray = cfg.defaultNamespaces
end
for _, namespace in ipairs(defaultnsArray) do
ndArgs[namespace] = cfg.pagetypes[namespace]
end
end
--[[
-- Add custom values passed in from the arguments. These overwrite the
-- defaults. The possible argument names are fetched from
-- Module:Namespace detect automatically in case new namespaces are
-- added. Although we accept namespace aliases as parameters, we only pass
-- the local namespace name as a parameter to Module:Namespace detect.
-- This means that the "image" parameter can overwrite defaults for the
-- File: namespace, which wouldn't work if we passed the parameters through
-- separately.
--]]
local mappings = getParamMappings()
for ns, paramAliases in pairs(mappings) do
-- Copy the aliases table, as # doesn't work with tables returned from
-- mw.loadData.
paramAliases = shallowCopy(paramAliases)
local paramName = paramAliases[1]
-- Iterate backwards along the array so that any values for the local
-- namespace names overwrite those for namespace aliases.
for i = #paramAliases, 1, -1 do
local paramAlias = paramAliases[i]
local ndArg = checkPagetypeInput(paramAlias, args[paramAlias])
if ndArg == false then
-- If any arguments are false, convert them to nil to protect
-- against breakage by future changes to
-- [[Module:Namespace detect]].
ndArgs[paramName] = nil
elseif ndArg then
ndArgs[paramName] = ndArg
end
end
end
-- Check for disambiguation-class and N/A-class pages in mainspace.
if ndArgs.main then
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "Dab" and "dab" will both match.
class = mw.ustring.lower(class)
end
local dab = getPagetypeFromClass(
class,
args[cfg.dab],
cfg.dabAliases,
cfg.dabDefault
)
if dab then
ndArgs.main = dab
else
local na = getPagetypeFromClass(
class,
args[cfg.na],
cfg.naAliases,
cfg.naDefault
)
if na then
ndArgs.main = na
end
end
end
-- If there is no talk value specified, use the corresponding subject
-- namespace for talk pages.
if not ndArgs.talk then
ndArgs.subjectns = true
end
-- Add the fallback value. This can also be customised, but it cannot be
-- disabled.
local other = args[cfg.other]
-- We will ignore true/false/nil results from yesno here, but using it
-- anyway for consistency.
other = yesno(other, other)
if type(other) == 'string' then
ndArgs.other = other
else
ndArgs.other = cfg.otherDefault
end
-- Allow custom page values.
ndArgs.page = args.page
return nsDetect(ndArgs)
end
local function detectRedirects(args)
local redirect = args[cfg.redirect]
-- The yesno function returns true/false for "yes", "no", etc., and returns
-- redirect for other input.
redirect = yesno(redirect, redirect)
if redirect == false then
-- Detect redirects unless they have been explicitly disallowed with
-- "redirect=no" or similar.
return
end
local pageObject = getPageObject(args.page)
-- If we are using subject namespaces elsewhere, do so here as well.
if pageObject
and not yesno(args.talk, true)
and args[cfg.defaultns] ~= cfg.defaultnsAll
then
pageObject = getPageObject(
pageObject.subjectNsText .. ':' .. pageObject.text
)
end
-- Allow custom values for redirects.
if pageObject and pageObject.isRedirect then
if type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
end
function p._main(args)
local redirect = detectRedirects(args)
local pagetype = ""
if redirect then
pagetype = redirect
else
pagetype = getNsDetectValue(args)
end
if yesno(args.plural, false) then
if cfg.irregularPlurals[pagetype] then
pagetype = cfg.irregularPlurals[pagetype]
else
pagetype = pagetype .. cfg.plural -- often 's'
end
end
if yesno(args.caps, false) then
pagetype = mw.ustring.upper(mw.ustring.sub(pagetype, 1, 1)) ..
mw.ustring.sub(pagetype, 2)
end
return pagetype
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
3uutklb10j89clizor7gnyrlkhbqg0d
281520
281519
2023-08-28T13:35:38Z
en>Mr. Stradivarius
0
Use [[Module:Disambiguation]] to detect disambiguation pages, and remove dependency on [[Module:Namespace detect]]. At the moment, this is using the config sandbox, but I will switch to the updated config shortly.
281520
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar --
-- templates. It automatically detects namespaces, and allows for a --
-- great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config/sandbox')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local mDisambiguation = require('Module:Disambiguation')
local p = {}
-- Look up a namespace argument in the args table.
local function lookUpNamespaceArg(args, key)
local arg = args[key]
-- Convert "yes", "1" etc. to true, "no", "0" etc. to false, and leave
-- other values the same.
return yesno(arg, arg)
end
-- Append multiple values to an array
local function appendMultiple(target, source)
for _, value in ipairs(source) do
table.insert(target, value)
end
end
-- Get argument keys for a title's namespace
local function getNamespaceArgKeys(title, cfg)
local nsInfo = mw.site.namespaces[title.namespace]
local customAliases = cfg.customNamespaceAliases[title.namespace] or {}
local keys = {}
if nsInfo.name ~= '' then
table.insert(keys, nsInfo.name)
end
if nsInfo.canonicalName ~= nsInfo.name and nsInfo.canonicalName ~= '' then
table.insert(keys, nsInfo.canonicalName)
end
appendMultiple(keys, nsInfo.aliases)
appendMultiple(keys, customAliases)
return keys
end
-- Get the argument for a title's namespace, if it was specified in the args
-- table.
local function getNamespaceArg(title, args, cfg)
if title.isTalkPage then
return lookUpNamespaceArg(args, cfg.talk)
end
for _, key in ipairs(getNamespaceArgKeys(title, cfg)) do
local arg = lookUpNamespaceArg(args, mw.ustring.lower(key))
if arg ~= nil then
return arg
end
end
return nil
end
-- Look up a page type specific to the title's namespace
local function getExplicitPageType(title, cfg)
if title.isTalkPage then
return cfg.talkDefault
else
return cfg.pagetypes[title.namespace]
end
end
-- Get a default page type that is not specific to the title's namespace
local function getDefaultPageType(args, cfg)
local other = lookUpNamespaceArg(args, cfg.other)
if type(other) == 'string' then
return other
else
return cfg.otherDefault
end
end
local function detectRedirects(title, args)
local redirect = lookUpNamespaceArg(args, cfg.redirect)
if redirect == false then
-- Don't detect redirects if they have been specifically disallowed.
return nil
end
-- Allow custom values for redirects.
if not title.isRedirect then
return nil
elseif type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
local function detectDisambiguationPages(title, args, cfg)
if title.namespace ~= 0 then
-- Only detect disambiguation pages in mainspace
return nil
end
local dab = lookUpNamespaceArg(args, cfg.dab)
if dab == false then
-- Don't detect disambiguation pages if explicitly disallowed
return nil
end
if not mDisambiguation.isDisambiguation(title:getContent()) then
return nil
elseif type(dab) == 'string' then
return dab
else
return cfg.dabDefault
end
end
-- Gets the pagetype from a class specified from the first positional
-- parameter.
local function getPageTypeFromClass(args, class, key, aliasTable, default)
local arg = lookUpNamespaceArg(args, key)
if arg == false then
-- Don't check for this class if it is specifically disallowed.
return nil
end
if aliasTable[class] then
if type(arg) == 'string' then
return arg
else
return default
end
end
return nil
end
-- Get page types for mainspaces pages with an explicit class specified
local function getMainNamespaceClassPageType(title, args, cfg)
if title.namespace ~= 0 then
return nil
end
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "na" and "NA" will both match.
class = mw.ustring.lower(class)
end
return getPageTypeFromClass(
args,
class,
cfg.na,
cfg.naAliases,
cfg.naDefault
)
end
-- Get page type specified by an explicit namespace argument.
local function getNamespaceArgPageType(title, args, cfg)
local namespaceArg = getNamespaceArg(title, args, cfg)
if namespaceArg == true then
-- Namespace has been explicitly enabled, so return the default for
-- this namespace
return getExplicitPageType(title, cfg)
elseif namespaceArg == false then
-- Namespace has been explicitly disabled
return getDefaultPageType(args, cfg)
elseif namespaceArg then
-- This namespaces uses custom text
return namespaceArg
else
return nil
end
end
-- Whether the title is in the set of default active namespaces which are
-- looked up in cfg.pagetypes.
local function isInDefaultActiveNamespace(title, args, cfg)
local defaultNamespacesKey = args[cfg.defaultns]
if defaultNamespacesKey == cfg.defaultnsAll then
return true
end
local defaultNamespaces
if defaultNamespacesKey == cfg.defaultnsExtended then
defaultNamespaces = cfg.extendedNamespaces
elseif defaultNamespacesKey == cfg.defaultnsNone then
defaultNamespaces = {}
else
defaultNamespaces = cfg.defaultNamespaces
end
return defaultNamespaces[title.namespace] or false
end
-- Get page type not specified or detected by other means
local function getOtherPageType(title, args, cfg)
if isInDefaultActiveNamespace(title, args, cfg) then
return getExplicitPageType(title, cfg)
else
return getDefaultPageType(args, cfg)
end
end
local function getPageType(title, args, cfg)
return (
detectRedirects(title, args, cfg)
or detectDisambiguationPages(title, args, cfg)
or getMainNamespaceClassPageType(title, args, cfg)
or getNamespaceArgPageType(title, args, cfg)
or getOtherPageType(title, args, cfg)
)
end
local function shouldUseSubjectTitle(args, cfg)
return not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll
end
-- Get the Scribunto title object to fetch the page type of
local function getTitle(args, cfg)
local title
if args.page then
title = mw.title.new(args.page)
if not title then
return nil
end
else
title = mw.title.getCurrentTitle()
end
if shouldUseSubjectTitle(args, cfg) then
return title.subjectPageTitle
else
return title
end
end
local function pluralize(pageType, cfg)
if cfg.irregularPlurals[pageType] then
return cfg.irregularPlurals[pageType]
else
return pageType .. cfg.plural -- often 's'
end
end
local function capitalize(pageType)
local first = mw.ustring.sub(pageType, 1, 1)
local rest = mw.ustring.sub(pageType, 2)
return mw.ustring.upper(first) .. rest
end
function p._main(args)
local title = getTitle(args, cfg)
local pageType = getPageType(title, args, cfg)
if yesno(args.plural, false) then
pageType = pluralize(pageType, cfg)
end
if yesno(args.caps, false) then
pageType = capitalize(pageType)
end
return pageType
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
evlqkuyqfnwuncqqrylfitmayl3ke1h
281521
281520
2023-08-28T13:37:34Z
en>Mr. Stradivarius
0
switch back to using [[Module:Pagetype/config]], as it has now been updated
281521
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- PAGETYPE --
-- --
-- This is a meta-module intended to replace {{pagetype}} and similar --
-- templates. It automatically detects namespaces, and allows for a --
-- great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local mDisambiguation = require('Module:Disambiguation')
local p = {}
-- Look up a namespace argument in the args table.
local function lookUpNamespaceArg(args, key)
local arg = args[key]
-- Convert "yes", "1" etc. to true, "no", "0" etc. to false, and leave
-- other values the same.
return yesno(arg, arg)
end
-- Append multiple values to an array
local function appendMultiple(target, source)
for _, value in ipairs(source) do
table.insert(target, value)
end
end
-- Get argument keys for a title's namespace
local function getNamespaceArgKeys(title, cfg)
local nsInfo = mw.site.namespaces[title.namespace]
local customAliases = cfg.customNamespaceAliases[title.namespace] or {}
local keys = {}
if nsInfo.name ~= '' then
table.insert(keys, nsInfo.name)
end
if nsInfo.canonicalName ~= nsInfo.name and nsInfo.canonicalName ~= '' then
table.insert(keys, nsInfo.canonicalName)
end
appendMultiple(keys, nsInfo.aliases)
appendMultiple(keys, customAliases)
return keys
end
-- Get the argument for a title's namespace, if it was specified in the args
-- table.
local function getNamespaceArg(title, args, cfg)
if title.isTalkPage then
return lookUpNamespaceArg(args, cfg.talk)
end
for _, key in ipairs(getNamespaceArgKeys(title, cfg)) do
local arg = lookUpNamespaceArg(args, mw.ustring.lower(key))
if arg ~= nil then
return arg
end
end
return nil
end
-- Look up a page type specific to the title's namespace
local function getExplicitPageType(title, cfg)
if title.isTalkPage then
return cfg.talkDefault
else
return cfg.pagetypes[title.namespace]
end
end
-- Get a default page type that is not specific to the title's namespace
local function getDefaultPageType(args, cfg)
local other = lookUpNamespaceArg(args, cfg.other)
if type(other) == 'string' then
return other
else
return cfg.otherDefault
end
end
local function detectRedirects(title, args)
local redirect = lookUpNamespaceArg(args, cfg.redirect)
if redirect == false then
-- Don't detect redirects if they have been specifically disallowed.
return nil
end
-- Allow custom values for redirects.
if not title.isRedirect then
return nil
elseif type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
local function detectDisambiguationPages(title, args, cfg)
if title.namespace ~= 0 then
-- Only detect disambiguation pages in mainspace
return nil
end
local dab = lookUpNamespaceArg(args, cfg.dab)
if dab == false then
-- Don't detect disambiguation pages if explicitly disallowed
return nil
end
if not mDisambiguation.isDisambiguation(title:getContent()) then
return nil
elseif type(dab) == 'string' then
return dab
else
return cfg.dabDefault
end
end
-- Gets the pagetype from a class specified from the first positional
-- parameter.
local function getPageTypeFromClass(args, class, key, aliasTable, default)
local arg = lookUpNamespaceArg(args, key)
if arg == false then
-- Don't check for this class if it is specifically disallowed.
return nil
end
if aliasTable[class] then
if type(arg) == 'string' then
return arg
else
return default
end
end
return nil
end
-- Get page types for mainspaces pages with an explicit class specified
local function getMainNamespaceClassPageType(title, args, cfg)
if title.namespace ~= 0 then
return nil
end
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "na" and "NA" will both match.
class = mw.ustring.lower(class)
end
return getPageTypeFromClass(
args,
class,
cfg.na,
cfg.naAliases,
cfg.naDefault
)
end
-- Get page type specified by an explicit namespace argument.
local function getNamespaceArgPageType(title, args, cfg)
local namespaceArg = getNamespaceArg(title, args, cfg)
if namespaceArg == true then
-- Namespace has been explicitly enabled, so return the default for
-- this namespace
return getExplicitPageType(title, cfg)
elseif namespaceArg == false then
-- Namespace has been explicitly disabled
return getDefaultPageType(args, cfg)
elseif namespaceArg then
-- This namespaces uses custom text
return namespaceArg
else
return nil
end
end
-- Whether the title is in the set of default active namespaces which are
-- looked up in cfg.pagetypes.
local function isInDefaultActiveNamespace(title, args, cfg)
local defaultNamespacesKey = args[cfg.defaultns]
if defaultNamespacesKey == cfg.defaultnsAll then
return true
end
local defaultNamespaces
if defaultNamespacesKey == cfg.defaultnsExtended then
defaultNamespaces = cfg.extendedNamespaces
elseif defaultNamespacesKey == cfg.defaultnsNone then
defaultNamespaces = {}
else
defaultNamespaces = cfg.defaultNamespaces
end
return defaultNamespaces[title.namespace] or false
end
-- Get page type not specified or detected by other means
local function getOtherPageType(title, args, cfg)
if isInDefaultActiveNamespace(title, args, cfg) then
return getExplicitPageType(title, cfg)
else
return getDefaultPageType(args, cfg)
end
end
local function getPageType(title, args, cfg)
return (
detectRedirects(title, args, cfg)
or detectDisambiguationPages(title, args, cfg)
or getMainNamespaceClassPageType(title, args, cfg)
or getNamespaceArgPageType(title, args, cfg)
or getOtherPageType(title, args, cfg)
)
end
local function shouldUseSubjectTitle(args, cfg)
return not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll
end
-- Get the Scribunto title object to fetch the page type of
local function getTitle(args, cfg)
local title
if args.page then
title = mw.title.new(args.page)
if not title then
return nil
end
else
title = mw.title.getCurrentTitle()
end
if shouldUseSubjectTitle(args, cfg) then
return title.subjectPageTitle
else
return title
end
end
local function pluralize(pageType, cfg)
if cfg.irregularPlurals[pageType] then
return cfg.irregularPlurals[pageType]
else
return pageType .. cfg.plural -- often 's'
end
end
local function capitalize(pageType)
local first = mw.ustring.sub(pageType, 1, 1)
local rest = mw.ustring.sub(pageType, 2)
return mw.ustring.upper(first) .. rest
end
function p._main(args)
local title = getTitle(args, cfg)
local pageType = getPageType(title, args, cfg)
if yesno(args.plural, false) then
pageType = pluralize(pageType, cfg)
end
if yesno(args.caps, false) then
pageType = capitalize(pageType)
end
return pageType
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
k0z1n0ukwz3xkek0ice2v07j7gv8tg1
281522
281521
2024-01-31T19:54:53Z
en>MSGJ
0
add functionality from [[Module:Disambiguation]], also add detection of soft redirects and articles marked as set index articles
281522
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- This meta-module which automatically detects namespaces, and allows --
-- for a great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local p = {}
-- Look up a namespace argument in the args table.
local function lookUpNamespaceArg(args, key)
local arg = args[key]
-- Convert "yes", "1" etc. to true, "no", "0" etc. to false, and leave
-- other values the same.
return yesno(arg, arg)
end
-- Append multiple values to an array
local function appendMultiple(target, source)
for _, value in ipairs(source) do
table.insert(target, value)
end
end
-- Get argument keys for a title's namespace
local function getNamespaceArgKeys(title, cfg)
local nsInfo = mw.site.namespaces[title.namespace]
local customAliases = cfg.customNamespaceAliases[title.namespace] or {}
local keys = {}
if nsInfo.name ~= '' then
table.insert(keys, nsInfo.name)
end
if nsInfo.canonicalName ~= nsInfo.name and nsInfo.canonicalName ~= '' then
table.insert(keys, nsInfo.canonicalName)
end
appendMultiple(keys, nsInfo.aliases)
appendMultiple(keys, customAliases)
return keys
end
-- Get the argument for a title's namespace, if it was specified in the args table.
local function getNamespaceArg(title, args, cfg)
if title.isTalkPage then
return lookUpNamespaceArg(args, cfg.talk)
end
for _, key in ipairs(getNamespaceArgKeys(title, cfg)) do
local arg = lookUpNamespaceArg(args, mw.ustring.lower(key))
if arg ~= nil then
return arg
end
end
return nil
end
-- Look up a page type specific to the title's namespace
local function getExplicitPageType(title, cfg)
if title.isTalkPage then
return cfg.talkDefault
else
return cfg.pagetypes[title.namespace]
end
end
-- Get a default page type that is not specific to the title's namespace
local function getDefaultPageType(args, cfg)
local other = lookUpNamespaceArg(args, cfg.other)
if type(other) == 'string' then
return other
else
return cfg.otherDefault
end
end
local function detectRedirects(title, args)
local redirect = lookUpNamespaceArg(args, cfg.redirect)
if redirect == false then
-- Don't detect redirects if they have been specifically disallowed.
return nil
end
-- Allow custom values for redirects.
if not title.isRedirect then
return nil
elseif type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
local function capitalize(pageType)
local first = mw.ustring.sub(pageType, 1, 1)
local rest = mw.ustring.sub(pageType, 2)
return mw.ustring.upper(first) .. rest
end
local function pluralize(pageType, cfg)
if cfg.irregularPlurals[pageType] then
return cfg.irregularPlurals[pageType]
else
return pageType .. cfg.plural -- often 's'
end
end
local function parseContent(title, args, list, parameter, default, articleOnly)
if articleOnly and title.namespace ~= 0 -- only detect in mainspace
or title.namespace == 828 and title.subpageText ~= 'doc' -- don't detect modules
then
return nil
end
local templates = mw.loadData('Module:Pagetype/' .. list)
local content = title:getContent()
if content == nil then
return nil
end
content = require('Module:Wikitext Parsing').PrepareText(content) -- disregard templates which do not have any affect
local templateNames = {}
local templateFound = false
for template in string.gmatch(content, "{{%s*([^|}]-)%s*[|}]") do
if templates[capitalize(template)] then
templateFound = true
break
end
end
local out = lookUpNamespaceArg(args, parameter)
if not templateFound then
return nil
elseif type(out)=='string' then
return out
else
return default
end
end
-- Gets the pagetype from a class specified from the first positional parameter.
local function getPageTypeFromClass(args, class, key, aliasTable, default)
local arg = lookUpNamespaceArg(args, key)
if arg == false then
-- Don't check for this class if it is specifically disallowed.
return nil
end
if aliasTable[class] then
if type(arg) == 'string' then
return arg
else
return default
end
end
return nil
end
-- Get page types for mainspaces pages with an explicit class specified
local function getMainNamespaceClassPageType(title, args, cfg)
if title.namespace ~= 0 then
return nil
end
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "na" and "NA" will both match.
class = mw.ustring.lower(class)
end
return getPageTypeFromClass(
args,
class,
cfg.na,
cfg.naAliases,
cfg.naDefault
)
end
-- Get page type specified by an explicit namespace argument.
local function getNamespaceArgPageType(title, args, cfg)
local namespaceArg = getNamespaceArg(title, args, cfg)
if namespaceArg == true then
-- Namespace has been explicitly enabled, so return the default for
-- this namespace
return getExplicitPageType(title, cfg)
elseif namespaceArg == false then
-- Namespace has been explicitly disabled
return getDefaultPageType(args, cfg)
elseif namespaceArg then
-- This namespaces uses custom text
return namespaceArg
else
return nil
end
end
-- Whether the title is in the set of default active namespaces which are looked up in cfg.pagetypes.
local function isInDefaultActiveNamespace(title, args, cfg)
local defaultNamespacesKey = args[cfg.defaultns]
if defaultNamespacesKey == cfg.defaultnsAll then
return true
end
local defaultNamespaces
if defaultNamespacesKey == cfg.defaultnsExtended then
defaultNamespaces = cfg.extendedNamespaces
elseif defaultNamespacesKey == cfg.defaultnsNone then
defaultNamespaces = {}
else
defaultNamespaces = cfg.defaultNamespaces
end
return defaultNamespaces[title.namespace] or false
end
-- Get page type not specified or detected by other means
local function getOtherPageType(title, args, cfg)
if isInDefaultActiveNamespace(title, args, cfg) then
return getExplicitPageType(title, cfg)
else
return getDefaultPageType(args, cfg)
end
end
local function getPageType(title, args, cfg)
return (
detectRedirects(title, args, cfg)
or parseContent(title, args, 'softredirect', cfg.softRedirect, cfg.softRedirectDefault)
or parseContent(title, args, 'setindex', cfg.sia, cfg.siaDefault, true)
or parseContent(title, args, 'disambiguation', cfg.dab, cfg.dabDefault, true)
or getMainNamespaceClassPageType(title, args, cfg)
or getNamespaceArgPageType(title, args, cfg)
or getOtherPageType(title, args, cfg)
)
end
local function shouldUseSubjectTitle(args, cfg)
return not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll
end
-- Get the Scribunto title object to fetch the page type of
local function getTitle(args, cfg)
local title
if args.page then
title = mw.title.new(args.page)
if not title then
return nil
end
else
title = mw.title.getCurrentTitle()
end
if shouldUseSubjectTitle(args, cfg) then
return title.subjectPageTitle
else
return title
end
end
function p._main(args)
local title = getTitle(args, cfg)
local pageType = getPageType(title, args, cfg)
if yesno(args.plural, false) then
pageType = pluralize(pageType, cfg)
end
if yesno(args.caps, false) then
pageType = capitalize(pageType)
end
return pageType
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
fmppxfc6xah78f1g9a6vsakm6pfwh9q
281523
281522
2024-02-03T14:03:49Z
en>Plastikspork
0
Add exist check per discussion
281523
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- This meta-module which automatically detects namespaces, and allows --
-- for a great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local p = {}
-- Look up a namespace argument in the args table.
local function lookUpNamespaceArg(args, key)
local arg = args[key]
-- Convert "yes", "1" etc. to true, "no", "0" etc. to false, and leave
-- other values the same.
return yesno(arg, arg)
end
-- Append multiple values to an array
local function appendMultiple(target, source)
for _, value in ipairs(source) do
table.insert(target, value)
end
end
-- Get argument keys for a title's namespace
local function getNamespaceArgKeys(title, cfg)
local nsInfo = mw.site.namespaces[title.namespace]
local customAliases = cfg.customNamespaceAliases[title.namespace] or {}
local keys = {}
if nsInfo.name ~= '' then
table.insert(keys, nsInfo.name)
end
if nsInfo.canonicalName ~= nsInfo.name and nsInfo.canonicalName ~= '' then
table.insert(keys, nsInfo.canonicalName)
end
appendMultiple(keys, nsInfo.aliases)
appendMultiple(keys, customAliases)
return keys
end
-- Get the argument for a title's namespace, if it was specified in the args table.
local function getNamespaceArg(title, args, cfg)
if title.isTalkPage then
return lookUpNamespaceArg(args, cfg.talk)
end
for _, key in ipairs(getNamespaceArgKeys(title, cfg)) do
local arg = lookUpNamespaceArg(args, mw.ustring.lower(key))
if arg ~= nil then
return arg
end
end
return nil
end
-- Look up a page type specific to the title's namespace
local function getExplicitPageType(title, cfg)
if title.isTalkPage then
return cfg.talkDefault
else
return cfg.pagetypes[title.namespace]
end
end
-- Get a default page type that is not specific to the title's namespace
local function getDefaultPageType(args, cfg)
local other = lookUpNamespaceArg(args, cfg.other)
if type(other) == 'string' then
return other
else
return cfg.otherDefault
end
end
local function detectRedirects(title, args)
local redirect = lookUpNamespaceArg(args, cfg.redirect)
if redirect == false then
-- Don't detect redirects if they have been specifically disallowed.
return nil
end
-- Allow custom values for redirects.
if not title.isRedirect then
return nil
elseif type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
local function capitalize(pageType)
local first = mw.ustring.sub(pageType, 1, 1)
local rest = mw.ustring.sub(pageType, 2)
return mw.ustring.upper(first) .. rest
end
local function pluralize(pageType, cfg)
if cfg.irregularPlurals[pageType] then
return cfg.irregularPlurals[pageType]
else
return pageType .. cfg.plural -- often 's'
end
end
local function parseContent(title, args, list, parameter, default, articleOnly)
if articleOnly and title.namespace~=0 -- only detect in mainspace
or title.namespace==828 and title.subpageText~='doc' -- don't detect modules
or not title.exists -- can't check unless page exists
then
return nil
end
local templates = mw.loadData('Module:Pagetype/' .. list)
local content = title:getContent()
if content == nil then
return nil
end
content = require('Module:Wikitext Parsing').PrepareText(content) -- disregard templates which do not have any affect
local templateNames = {}
local templateFound = false
for template in string.gmatch(content, "{{%s*([^|}]-)%s*[|}]") do
if templates[capitalize(template)] then
templateFound = true
break
end
end
local out = lookUpNamespaceArg(args, parameter)
if not templateFound then
return nil
elseif type(out)=='string' then
return out
else
return default
end
end
-- Gets the pagetype from a class specified from the first positional parameter.
local function getPageTypeFromClass(args, class, key, aliasTable, default)
local arg = lookUpNamespaceArg(args, key)
if arg == false then
-- Don't check for this class if it is specifically disallowed.
return nil
end
if aliasTable[class] then
if type(arg) == 'string' then
return arg
else
return default
end
end
return nil
end
-- Get page types for mainspaces pages with an explicit class specified
local function getMainNamespaceClassPageType(title, args, cfg)
if title.namespace ~= 0 then
return nil
end
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "na" and "NA" will both match.
class = mw.ustring.lower(class)
end
return getPageTypeFromClass(
args,
class,
cfg.na,
cfg.naAliases,
cfg.naDefault
)
end
-- Get page type specified by an explicit namespace argument.
local function getNamespaceArgPageType(title, args, cfg)
local namespaceArg = getNamespaceArg(title, args, cfg)
if namespaceArg == true then
-- Namespace has been explicitly enabled, so return the default for
-- this namespace
return getExplicitPageType(title, cfg)
elseif namespaceArg == false then
-- Namespace has been explicitly disabled
return getDefaultPageType(args, cfg)
elseif namespaceArg then
-- This namespaces uses custom text
return namespaceArg
else
return nil
end
end
-- Whether the title is in the set of default active namespaces which are looked up in cfg.pagetypes.
local function isInDefaultActiveNamespace(title, args, cfg)
local defaultNamespacesKey = args[cfg.defaultns]
if defaultNamespacesKey == cfg.defaultnsAll then
return true
end
local defaultNamespaces
if defaultNamespacesKey == cfg.defaultnsExtended then
defaultNamespaces = cfg.extendedNamespaces
elseif defaultNamespacesKey == cfg.defaultnsNone then
defaultNamespaces = {}
else
defaultNamespaces = cfg.defaultNamespaces
end
return defaultNamespaces[title.namespace] or false
end
-- Get page type not specified or detected by other means
local function getOtherPageType(title, args, cfg)
if isInDefaultActiveNamespace(title, args, cfg) then
return getExplicitPageType(title, cfg)
else
return getDefaultPageType(args, cfg)
end
end
local function getPageType(title, args, cfg)
return (
detectRedirects(title, args, cfg)
or parseContent(title, args, 'softredirect', cfg.softRedirect, cfg.softRedirectDefault)
or parseContent(title, args, 'setindex', cfg.sia, cfg.siaDefault, true)
or parseContent(title, args, 'disambiguation', cfg.dab, cfg.dabDefault, true)
or getMainNamespaceClassPageType(title, args, cfg)
or getNamespaceArgPageType(title, args, cfg)
or getOtherPageType(title, args, cfg)
)
end
local function shouldUseSubjectTitle(args, cfg)
return not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll
end
-- Get the Scribunto title object to fetch the page type of
local function getTitle(args, cfg)
local title
if args.page then
title = mw.title.new(args.page)
if not title then
return nil
end
else
title = mw.title.getCurrentTitle()
end
if shouldUseSubjectTitle(args, cfg) then
return title.subjectPageTitle
else
return title
end
end
function p._main(args)
local title = getTitle(args, cfg)
local pageType = getPageType(title, args, cfg)
if yesno(args.plural, false) then
pageType = pluralize(pageType, cfg)
end
if yesno(args.caps, false) then
pageType = capitalize(pageType)
end
return pageType
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
cp7bkm934lophzc0y7w42l1rtxnf5q0
281524
281523
2024-03-22T19:27:12Z
en>MSGJ
0
performance improvements and bug fix by [[User:Aidan9382]]
281524
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- This meta-module which automatically detects namespaces, and allows --
-- for a great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local p = {}
-- Look up a namespace argument in the args table.
local function lookUpNamespaceArg(args, key)
local arg = args[key]
-- Convert "yes", "1" etc. to true, "no", "0" etc. to false, and leave
-- other values the same.
return yesno(arg, arg)
end
-- Append multiple values to an array
local function appendMultiple(target, source)
for _, value in ipairs(source) do
table.insert(target, value)
end
end
-- Get argument keys for a title's namespace
local function getNamespaceArgKeys(title, cfg)
local nsInfo = mw.site.namespaces[title.namespace]
local customAliases = cfg.customNamespaceAliases[title.namespace] or {}
local keys = {}
if nsInfo.name ~= '' then
table.insert(keys, nsInfo.name)
end
if nsInfo.canonicalName ~= nsInfo.name and nsInfo.canonicalName ~= '' then
table.insert(keys, nsInfo.canonicalName)
end
appendMultiple(keys, nsInfo.aliases)
appendMultiple(keys, customAliases)
return keys
end
-- Get the argument for a title's namespace, if it was specified in the args table.
local function getNamespaceArg(title, args, cfg)
if title.isTalkPage then
return lookUpNamespaceArg(args, cfg.talk)
end
for _, key in ipairs(getNamespaceArgKeys(title, cfg)) do
local arg = lookUpNamespaceArg(args, mw.ustring.lower(key))
if arg ~= nil then
return arg
end
end
return nil
end
-- Look up a page type specific to the title's namespace
local function getExplicitPageType(title, cfg)
if title.isTalkPage then
return cfg.talkDefault
else
return cfg.pagetypes[title.namespace]
end
end
-- Get a default page type that is not specific to the title's namespace
local function getDefaultPageType(args, cfg)
local other = lookUpNamespaceArg(args, cfg.other)
if type(other) == 'string' then
return other
else
return cfg.otherDefault
end
end
local function detectRedirects(title, args)
local redirect = lookUpNamespaceArg(args, cfg.redirect)
if redirect == false then
-- Don't detect redirects if they have been specifically disallowed.
return nil
end
-- Allow custom values for redirects.
if not title.isRedirect then
return nil
elseif type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
local function capitalize(pageType)
local first = mw.ustring.sub(pageType, 1, 1)
local rest = mw.ustring.sub(pageType, 2)
return mw.ustring.upper(first) .. rest
end
local function pluralize(pageType, cfg)
if cfg.irregularPlurals[pageType] then
return cfg.irregularPlurals[pageType]
else
return pageType .. cfg.plural -- often 's'
end
end
local function parseContent(title, args, optionsList)
if title.namespace==828 and title.subpageText~='doc' -- don't detect modules
or not title.exists -- can't check unless page exists
then
return nil
end
local content = title:getContent()
if content == nil then
return nil
end
local templates -- lazily evaluated
for _, options in next, optionsList do
local list, parameter, default, articleOnly = unpack(options, 1, 4)
if not articleOnly or title.namespace==0 then -- only check for templates if we should...
local out = lookUpNamespaceArg(args, parameter)
if type(out) == "string" or (out ~= false and default) then -- ...and if we actually have anything to say about them
if not templates then
templates = {} -- do our delayed evaluation now that we are required to
content = require('Module:Wikitext Parsing').PrepareText(content) -- disregard templates which do not have any affect
for template in string.gmatch(content, "{{%s*([^|}]-)%s*[|}]") do
templates[#templates+1] = capitalize(template)
end
end
local wantedTemplates = mw.loadData('Module:Pagetype/' .. list)
local templateFound = false
for _, template in next, templates do
if wantedTemplates[template] then
templateFound = true
break
end
end
if templateFound then
if type(out)=='string' then
return out
elseif out ~= false and default then
return default
end
end
end
end
end
end
-- Gets the pagetype from a class specified from the first positional parameter.
local function getPageTypeFromClass(args, class, key, aliasTable, default)
local arg = lookUpNamespaceArg(args, key)
if arg == false then
-- Don't check for this class if it is specifically disallowed.
return nil
end
if aliasTable[class] then
if type(arg) == 'string' then
return arg
else
return default
end
end
return nil
end
-- Get page types for mainspaces pages with an explicit class specified
local function getMainNamespaceClassPageType(title, args, cfg)
if title.namespace ~= 0 then
return nil
end
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "na" and "NA" will both match.
class = mw.ustring.lower(class)
end
return getPageTypeFromClass(
args,
class,
cfg.na,
cfg.naAliases,
cfg.naDefault
)
end
-- Get page type specified by an explicit namespace argument.
local function getNamespaceArgPageType(title, args, cfg)
local namespaceArg = getNamespaceArg(title, args, cfg)
if namespaceArg == true then
-- Namespace has been explicitly enabled, so return the default for
-- this namespace
return getExplicitPageType(title, cfg)
elseif namespaceArg == false then
-- Namespace has been explicitly disabled
return getDefaultPageType(args, cfg)
elseif namespaceArg then
-- This namespaces uses custom text
return namespaceArg
else
return nil
end
end
-- Whether the title is in the set of default active namespaces which are looked up in cfg.pagetypes.
local function isInDefaultActiveNamespace(title, args, cfg)
local defaultNamespacesKey = args[cfg.defaultns]
if defaultNamespacesKey == cfg.defaultnsAll then
return true
end
local defaultNamespaces
if defaultNamespacesKey == cfg.defaultnsExtended then
defaultNamespaces = cfg.extendedNamespaces
elseif defaultNamespacesKey == cfg.defaultnsNone then
defaultNamespaces = {}
else
defaultNamespaces = cfg.defaultNamespaces
end
return defaultNamespaces[title.namespace] or false
end
-- Get page type not specified or detected by other means
local function getOtherPageType(title, args, cfg)
if isInDefaultActiveNamespace(title, args, cfg) then
return getExplicitPageType(title, cfg)
else
return getDefaultPageType(args, cfg)
end
end
local function getPageType(title, args, cfg)
return (
detectRedirects(title, args, cfg)
or parseContent(title, args, {
{'softredirect', cfg.softRedirect, cfg.softRedirectDefault},
{'setindex', cfg.sia, cfg.siaDefault, true},
{'disambiguation', cfg.dab, cfg.dabDefault, true},
})
or getMainNamespaceClassPageType(title, args, cfg)
or getNamespaceArgPageType(title, args, cfg)
or getOtherPageType(title, args, cfg)
)
end
local function shouldUseSubjectTitle(args, cfg)
return not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll
end
-- Get the Scribunto title object to fetch the page type of
local function getTitle(args, cfg)
local title
if args.page then
title = mw.title.new(args.page)
if not title then
return nil
end
else
title = mw.title.getCurrentTitle()
end
if shouldUseSubjectTitle(args, cfg) then
return title.subjectPageTitle
else
return title
end
end
function p._main(args)
local title = getTitle(args, cfg)
local pageType = getPageType(title, args, cfg)
if yesno(args.plural, false) then
pageType = pluralize(pageType, cfg)
end
if yesno(args.caps, false) then
pageType = capitalize(pageType)
end
return pageType
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
8nmyrg4ztbmhksoz09ej8l64pxuaryu
281525
281524
2024-05-09T08:51:08Z
en>MSGJ
0
add detection of redirects under discussion at rfd
281525
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- This meta-module which automatically detects namespaces, and allows --
-- for a great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local p = {}
-- Look up a namespace argument in the args table.
local function lookUpNamespaceArg(args, key)
local arg = args[key]
-- Convert "yes", "1" etc. to true, "no", "0" etc. to false, and leave
-- other values the same.
return yesno(arg, arg)
end
-- Append multiple values to an array
local function appendMultiple(target, source)
for _, value in ipairs(source) do
table.insert(target, value)
end
end
-- Get argument keys for a title's namespace
local function getNamespaceArgKeys(title, cfg)
local nsInfo = mw.site.namespaces[title.namespace]
local customAliases = cfg.customNamespaceAliases[title.namespace] or {}
local keys = {}
if nsInfo.name ~= '' then
table.insert(keys, nsInfo.name)
end
if nsInfo.canonicalName ~= nsInfo.name and nsInfo.canonicalName ~= '' then
table.insert(keys, nsInfo.canonicalName)
end
appendMultiple(keys, nsInfo.aliases)
appendMultiple(keys, customAliases)
return keys
end
-- Get the argument for a title's namespace, if it was specified in the args table.
local function getNamespaceArg(title, args, cfg)
if title.isTalkPage then
return lookUpNamespaceArg(args, cfg.talk)
end
for _, key in ipairs(getNamespaceArgKeys(title, cfg)) do
local arg = lookUpNamespaceArg(args, mw.ustring.lower(key))
if arg ~= nil then
return arg
end
end
return nil
end
-- Look up a page type specific to the title's namespace
local function getExplicitPageType(title, cfg)
if title.isTalkPage then
return cfg.talkDefault
else
return cfg.pagetypes[title.namespace]
end
end
-- Get a default page type that is not specific to the title's namespace
local function getDefaultPageType(args, cfg)
local other = lookUpNamespaceArg(args, cfg.other)
if type(other) == 'string' then
return other
else
return cfg.otherDefault
end
end
local function detectRedirects(title, args)
local redirect = lookUpNamespaceArg(args, cfg.redirect)
if redirect == false then
-- Don't detect redirects if they have been specifically disallowed.
return nil
end
-- Allow custom values for redirects.
if not title.isRedirect then
return nil
elseif type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
local function capitalize(pageType)
local first = mw.ustring.sub(pageType, 1, 1)
local rest = mw.ustring.sub(pageType, 2)
return mw.ustring.upper(first) .. rest
end
local function pluralize(pageType, cfg)
if cfg.irregularPlurals[pageType] then
return cfg.irregularPlurals[pageType]
else
return pageType .. cfg.plural -- often 's'
end
end
local function parseContent(title, args, optionsList)
if title.namespace==828 and title.subpageText~='doc' -- don't detect modules
or not title.exists -- can't check unless page exists
then
return nil
end
local content = title:getContent()
if content == nil then
return nil
end
local templates -- lazily evaluated
for _, options in next, optionsList do
local list, parameter, default, articleOnly = unpack(options, 1, 4)
if not articleOnly or title.namespace==0 then -- only check for templates if we should...
local out = lookUpNamespaceArg(args, parameter)
if type(out) == "string" or (out ~= false and default) then -- ...and if we actually have anything to say about them
if not templates then
templates = {} -- do our delayed evaluation now that we are required to
content = require('Module:Wikitext Parsing').PrepareText(content) -- disregard templates which do not have any affect
for template in string.gmatch(content, "{{%s*([^|}]-)%s*[|}]") do
templates[#templates+1] = capitalize(template)
end
end
local wantedTemplates = mw.loadData('Module:Pagetype/' .. list)
local templateFound = false
for _, template in next, templates do
if wantedTemplates[template] then
templateFound = true
break
end
end
if templateFound then
if type(out)=='string' then
return out
elseif out ~= false and default then
return default
end
end
end
end
end
end
-- Gets the pagetype from a class specified from the first positional parameter.
local function getPageTypeFromClass(args, class, key, aliasTable, default)
local arg = lookUpNamespaceArg(args, key)
if arg == false then
-- Don't check for this class if it is specifically disallowed.
return nil
end
if aliasTable[class] then
if type(arg) == 'string' then
return arg
else
return default
end
end
return nil
end
-- Get page types for mainspaces pages with an explicit class specified
local function getMainNamespaceClassPageType(title, args, cfg)
if title.namespace ~= 0 then
return nil
end
local class = args[1]
if type(class) == 'string' then
-- Put in lower case so e.g. "na" and "NA" will both match.
class = mw.ustring.lower(class)
end
return getPageTypeFromClass(
args,
class,
cfg.na,
cfg.naAliases,
cfg.naDefault
)
end
-- Get page type specified by an explicit namespace argument.
local function getNamespaceArgPageType(title, args, cfg)
local namespaceArg = getNamespaceArg(title, args, cfg)
if namespaceArg == true then
-- Namespace has been explicitly enabled, so return the default for
-- this namespace
return getExplicitPageType(title, cfg)
elseif namespaceArg == false then
-- Namespace has been explicitly disabled
return getDefaultPageType(args, cfg)
elseif namespaceArg then
-- This namespaces uses custom text
return namespaceArg
else
return nil
end
end
-- Whether the title is in the set of default active namespaces which are looked up in cfg.pagetypes.
local function isInDefaultActiveNamespace(title, args, cfg)
local defaultNamespacesKey = args[cfg.defaultns]
if defaultNamespacesKey == cfg.defaultnsAll then
return true
end
local defaultNamespaces
if defaultNamespacesKey == cfg.defaultnsExtended then
defaultNamespaces = cfg.extendedNamespaces
elseif defaultNamespacesKey == cfg.defaultnsNone then
defaultNamespaces = {}
else
defaultNamespaces = cfg.defaultNamespaces
end
return defaultNamespaces[title.namespace] or false
end
-- Get page type not specified or detected by other means
local function getOtherPageType(title, args, cfg)
if isInDefaultActiveNamespace(title, args, cfg) then
return getExplicitPageType(title, cfg)
else
return getDefaultPageType(args, cfg)
end
end
local function getPageType(title, args, cfg)
return (
detectRedirects(title, args, cfg)
or parseContent(title, args, {
{'softredirect', cfg.softRedirect, cfg.softRedirectDefault},
{'setindex', cfg.sia, cfg.siaDefault, true},
{'disambiguation', cfg.dab, cfg.dabDefault, true},
{'rfd', cfg.rfd, cfg.rfdDefault},
})
or getMainNamespaceClassPageType(title, args, cfg)
or getNamespaceArgPageType(title, args, cfg)
or getOtherPageType(title, args, cfg)
)
end
local function shouldUseSubjectTitle(args, cfg)
return not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll
end
-- Get the Scribunto title object to fetch the page type of
local function getTitle(args, cfg)
local title
if args.page then
title = mw.title.new(args.page)
if not title then
return nil
end
else
title = mw.title.getCurrentTitle()
end
if shouldUseSubjectTitle(args, cfg) then
return title.subjectPageTitle
else
return title
end
end
function p._main(args)
local title = getTitle(args, cfg)
local pageType = getPageType(title, args, cfg)
if yesno(args.plural, false) then
pageType = pluralize(pageType, cfg)
end
if yesno(args.caps, false) then
pageType = capitalize(pageType)
end
return pageType
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
i3opdk6uz4bhkdm6d9086ir7ufhxyqb
281526
281525
2024-05-12T21:45:43Z
en>MSGJ
0
non-existent pages are not articles, plus some code simplification
281526
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- This meta-module which automatically detects namespaces, and allows --
-- for a great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local p = {}
-- Look up a namespace argument in the args table.
local function lookUpNamespaceArg(args, key)
local arg = args[key]
-- Convert "yes", "1" etc. to true, "no", "0" etc. to false, and leave
-- other values the same.
return yesno(arg, arg)
end
-- Append multiple values to an array
local function appendMultiple(target, source)
for _, value in ipairs(source) do
table.insert(target, value)
end
end
-- Get argument keys for a title's namespace
local function getNamespaceArgKeys(title, cfg)
local nsInfo = mw.site.namespaces[title.namespace]
local customAliases = cfg.customNamespaceAliases[title.namespace] or {}
local keys = {}
if nsInfo.name ~= '' then
table.insert(keys, nsInfo.name)
end
if nsInfo.canonicalName ~= nsInfo.name and nsInfo.canonicalName ~= '' then
table.insert(keys, nsInfo.canonicalName)
end
appendMultiple(keys, nsInfo.aliases)
appendMultiple(keys, customAliases)
return keys
end
-- Get the argument for a title's namespace, if it was specified in the args table.
local function getNamespaceArg(title, args, cfg)
if title.isTalkPage then
return lookUpNamespaceArg(args, cfg.talk)
end
for _, key in ipairs(getNamespaceArgKeys(title, cfg)) do
local arg = lookUpNamespaceArg(args, mw.ustring.lower(key))
if arg ~= nil then
return arg
end
end
return nil
end
-- Look up a page type specific to the title's namespace
local function getExplicitPageType(title, cfg)
if title.isTalkPage then
return cfg.talkDefault
else
return cfg.pagetypes[title.namespace]
end
end
-- Get a default page type that is not specific to the title's namespace
local function getDefaultPageType(args, cfg)
local other = lookUpNamespaceArg(args, cfg.other)
if type(other) == 'string' then
return other
else
return cfg.otherDefault
end
end
local function detectRedirects(title, args)
local redirect = lookUpNamespaceArg(args, cfg.redirect)
if redirect == false then
-- Don't detect redirects if they have been specifically disallowed.
return nil
end
-- Allow custom values for redirects.
if not title.isRedirect then
return nil
elseif type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
local function capitalize(pageType)
local first = mw.ustring.sub(pageType, 1, 1)
local rest = mw.ustring.sub(pageType, 2)
return mw.ustring.upper(first) .. rest
end
local function pluralize(pageType, cfg)
if cfg.irregularPlurals[pageType] then
return cfg.irregularPlurals[pageType]
else
return pageType .. cfg.plural -- often 's'
end
end
local function parseContent(title, args, optionsList)
if title.namespace==828 and title.subpageText~='doc' -- don't detect modules
or not title.exists -- can't check unless page exists
then
return nil
end
local content = title:getContent()
if content == nil then
return nil
end
local templates -- lazily evaluated
for _, options in next, optionsList do
local list, parameter, default, articleOnly = unpack(options, 1, 4)
if not articleOnly or title.namespace==0 then -- only check for templates if we should...
local out = lookUpNamespaceArg(args, parameter)
if type(out) == "string" or (out ~= false and default) then -- ...and if we actually have anything to say about them
if not templates then
templates = {} -- do our delayed evaluation now that we are required to
content = require('Module:Wikitext Parsing').PrepareText(content) -- disregard templates which do not have any affect
for template in string.gmatch(content, "{{%s*([^|}]-)%s*[|}]") do
templates[#templates+1] = capitalize(template)
end
end
local wantedTemplates = mw.loadData('Module:Pagetype/' .. list)
local templateFound = false
for _, template in next, templates do
if wantedTemplates[template] then
templateFound = true
break
end
end
if templateFound then
if type(out)=='string' then
return out
elseif out ~= false and default then
return default
end
end
end
end
end
end
-- Get page types for mainspaces pages with an explicit class specified
local function getMainNamespaceClassPageType(title, args, cfg)
if not title.exists then -- not an article if it does not exist
return cfg.naDefault
end
local class = args[1]
if type(class) == 'string' then -- Put in lower case so e.g. "na" and "NA" will both match
class = mw.ustring.lower(class)
end
local arg = lookUpNamespaceArg(args, cfg.na)
if arg == false then -- don't check for this class if it is specifically disallowed
return nil
end
if cfg.naAliases[class] then
if type(arg) == 'string' then
return arg
else
return cfg.naDefault
end
else
return nil
end
end
-- Get page type specified by an explicit namespace argument.
local function getNamespaceArgPageType(title, args, cfg)
local namespaceArg = getNamespaceArg(title, args, cfg)
if namespaceArg == true then
-- Namespace has been explicitly enabled, so return the default for
-- this namespace
return getExplicitPageType(title, cfg)
elseif namespaceArg == false then
-- Namespace has been explicitly disabled
return getDefaultPageType(args, cfg)
elseif namespaceArg then
-- This namespaces uses custom text
return namespaceArg
else
return nil
end
end
-- Get page type not specified or detected by other means
local function getOtherPageType(title, args, cfg)
-- Whether the title is in the set of default active namespaces which are looked up in cfg.pagetypes.
local isInDefaultActiveNamespace = false
local defaultNamespacesKey = args[cfg.defaultns]
if defaultNamespacesKey == cfg.defaultnsAll then
isInDefaultActiveNamespace = true
else
local defaultNamespaces
if defaultNamespacesKey == cfg.defaultnsExtended then
defaultNamespaces = cfg.extendedNamespaces
elseif defaultNamespacesKey == cfg.defaultnsNone then
defaultNamespaces = {}
else
defaultNamespaces = cfg.defaultNamespaces
end
isInDefaultActiveNamespace = defaultNamespaces[title.namespace]
end
if isInDefaultActiveNamespace then
return getExplicitPageType(title, cfg)
else
return getDefaultPageType(args, cfg)
end
end
local function getPageType(title, args, cfg)
return (
detectRedirects(title, args, cfg)
or parseContent(title, args, {
{'softredirect', cfg.softRedirect, cfg.softRedirectDefault},
{'setindex', cfg.sia, cfg.siaDefault, true},
{'disambiguation', cfg.dab, cfg.dabDefault, true},
{'rfd', cfg.rfd, cfg.rfdDefault},
})
or (title.namespace == 0 and getMainNamespaceClassPageType(title, args, cfg))
or getNamespaceArgPageType(title, args, cfg)
or getOtherPageType(title, args, cfg)
)
end
-- Get the Scribunto title object to fetch the page type of
local function getTitle(args, cfg)
local title
if args.page then
title = mw.title.new(args.page)
if not title then
return nil
end
else
title = mw.title.getCurrentTitle()
end
if not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll then
return title.subjectPageTitle
else
return title
end
end
function p._main(args)
local title = getTitle(args, cfg)
local pageType = getPageType(title, args, cfg)
if yesno(args.plural, false) then
pageType = pluralize(pageType, cfg)
end
if yesno(args.caps, false) then
pageType = capitalize(pageType)
end
return pageType
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
return p
t8rrz83u3l3dao798hd350mmvsgdooq
281527
281526
2024-05-16T12:25:41Z
en>MSGJ
0
add detection of non-existent pages, some code simplication
281527
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- This meta-module which automatically detects namespaces, and allows --
-- for a great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local yesno = require('Module:Yesno')
local p = {}
-- Look up a namespace argument in the args table.
local function lookUpNamespaceArg(args, key)
local arg = args[key]
-- Convert "yes", "1" etc. to true, "no", "0" etc. to false, and leave
-- other values the same.
return yesno(arg, arg)
end
-- Append multiple values to an array
local function appendMultiple(target, source)
for _, value in ipairs(source) do
table.insert(target, value)
end
end
-- Get argument keys for a title's namespace
local function getNamespaceArgKeys(title)
local nsInfo = mw.site.namespaces[title.namespace]
local customAliases = cfg.customNamespaceAliases[title.namespace] or {}
local keys = {}
if nsInfo.name ~= '' then
table.insert(keys, nsInfo.name)
end
if nsInfo.canonicalName ~= nsInfo.name and nsInfo.canonicalName ~= '' then
table.insert(keys, nsInfo.canonicalName)
end
appendMultiple(keys, nsInfo.aliases)
appendMultiple(keys, customAliases)
return keys
end
-- Get the argument for a title's namespace, if it was specified in the args table.
local function getNamespaceArg(title, args)
if title.isTalkPage then
return lookUpNamespaceArg(args, cfg.talk)
end
for _, key in ipairs(getNamespaceArgKeys(title)) do
local arg = lookUpNamespaceArg(args, mw.ustring.lower(key))
if arg ~= nil then
return arg
end
end
return nil
end
-- Look up a page type specific to the title's namespace
local function getExplicitPageType(title)
if title.isTalkPage then
return cfg.talkDefault
else
return cfg.pagetypes[title.namespace]
end
end
-- Get a default page type that is not specific to the title's namespace
local function getDefaultPageType(args)
local other = lookUpNamespaceArg(args, cfg.other)
if type(other) == 'string' then
return other
else
return cfg.otherDefault
end
end
local function detectRedirects(title, args)
local redirect = lookUpNamespaceArg(args, cfg.redirect)
if redirect == false then
-- Don't detect redirects if they have been specifically disallowed.
return nil
end
-- Allow custom values for redirects.
if not title.isRedirect then
return nil
elseif type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
local function capitalize(pageType)
local first = mw.ustring.sub(pageType, 1, 1)
local rest = mw.ustring.sub(pageType, 2)
return mw.ustring.upper(first) .. rest
end
local function pluralize(pageType)
if cfg.irregularPlurals[pageType] then
return cfg.irregularPlurals[pageType]
else
return pageType .. cfg.plural -- often 's'
end
end
local function parseContent(title, args, optionsList)
if title.namespace==828 and title.subpageText~='doc' -- don't detect modules
or not title.exists -- can't check unless page exists
then
return nil
end
local content = title:getContent()
if content == nil then
return nil
end
local templates -- lazily evaluated
for _, options in next, optionsList do
local list, parameter, default, articleOnly = unpack(options, 1, 4)
if not articleOnly or title.namespace==0 then -- only check for templates if we should...
local out = lookUpNamespaceArg(args, parameter)
if type(out) == "string" or (out ~= false and default) then -- ...and if we actually have anything to say about them
if not templates then
templates = {} -- do our delayed evaluation now that we are required to
content = require('Module:Wikitext Parsing').PrepareText(content) -- disregard templates which do not have any affect
for template in string.gmatch(content, "{{%s*([^|}]-)%s*[|}]") do
templates[#templates+1] = capitalize(template)
end
end
local wantedTemplates = mw.loadData('Module:Pagetype/' .. list)
local templateFound = false
for _, template in next, templates do
if wantedTemplates[template] then
templateFound = true
break
end
end
if templateFound then
if type(out)=='string' then
return out
elseif out ~= false and default then
return default
end
end
end
end
end
end
-- Find pages which do not exist
local function nonExistent(title, args)
local arg = lookUpNamespaceArg(args, cfg.ne)
if arg == false then
return nil
end
if not title.exists then -- not an article if it does not exist
if type(arg) == 'string' then
return arg
else
return cfg.naDefault
end
end
end
-- Get page types for mainspaces pages with an explicit class specified
local function getMainNamespaceClassPageType(title, args)
local class = args[1]
if type(class) == 'string' then -- Put in lower case so e.g. "na" and "NA" will both match
class = mw.ustring.lower(class)
end
local arg = lookUpNamespaceArg(args, cfg.na)
if arg == false then -- don't check for this class if it is specifically disallowed
return nil
end
if cfg.naAliases[class] then
if type(arg) == 'string' then
return arg
else
return cfg.naDefault
end
else
return nil
end
end
-- Get page type specified by an explicit namespace argument.
local function getNamespaceArgPageType(title, args)
local namespaceArg = getNamespaceArg(title, args)
if namespaceArg == true then
-- Namespace has been explicitly enabled, so return the default for
-- this namespace
return getExplicitPageType(title)
elseif namespaceArg == false then
-- Namespace has been explicitly disabled
return getDefaultPageType(args)
elseif namespaceArg then
-- This namespaces uses custom text
return namespaceArg
else
return nil
end
end
-- Get page type not specified or detected by other means
local function getOtherPageType(title, args)
-- Whether the title is in the set of default active namespaces which are looked up in cfg.pagetypes.
local isInDefaultActiveNamespace = false
local defaultNamespacesKey = args[cfg.defaultns]
if defaultNamespacesKey == cfg.defaultnsAll then
isInDefaultActiveNamespace = true
else
local defaultNamespaces
if defaultNamespacesKey == cfg.defaultnsExtended then
defaultNamespaces = cfg.extendedNamespaces
elseif defaultNamespacesKey == cfg.defaultnsNone then
defaultNamespaces = {}
else
defaultNamespaces = cfg.defaultNamespaces
end
isInDefaultActiveNamespace = defaultNamespaces[title.namespace]
end
if isInDefaultActiveNamespace then
return getExplicitPageType(title)
else
return getDefaultPageType(args)
end
end
function p._main(args)
local title
if args.page then
title = mw.title.new(args.page)
else
title = mw.title.getCurrentTitle()
end
if title and not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll then
title = title.subjectPageTitle
end
local pageType = detectRedirects(title, args)
or nonExistent(title, args)
or parseContent(title, args, {
{'softredirect', cfg.softRedirect, cfg.softRedirectDefault},
{'setindex', cfg.sia, cfg.siaDefault, true},
{'disambiguation', cfg.dab, cfg.dabDefault, true},
{'rfd', cfg.rfd, cfg.rfdDefault},
})
or (title.namespace == 0 and getMainNamespaceClassPageType(title, args))
or getNamespaceArgPageType(title, args)
or getOtherPageType(title, args)
if yesno(args.plural, false) then
pageType = pluralize(pageType)
end
if yesno(args.caps, false) then
pageType = capitalize(pageType)
end
return pageType
end
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame)
return p._main(args)
end
return p
kp6jtkl1kqx5yf4bm51odqibgotexx0
281528
281527
2024-05-21T17:35:20Z
en>MSGJ
0
fix for files and interface messages which do exist but are not stored locally
281528
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- This meta-module which automatically detects namespaces, and allows --
-- for a great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local yesno = require('Module:Yesno')
local p = {}
-- Look up a namespace argument in the args table.
local function lookUpNamespaceArg(args, key)
local arg = args[key]
-- Convert "yes", "1" etc. to true, "no", "0" etc. to false, and leave
-- other values the same.
return yesno(arg, arg)
end
-- Append multiple values to an array
local function appendMultiple(target, source)
for _, value in ipairs(source) do
table.insert(target, value)
end
end
-- Get argument keys for a title's namespace
local function getNamespaceArgKeys(title)
local nsInfo = mw.site.namespaces[title.namespace]
local customAliases = cfg.customNamespaceAliases[title.namespace] or {}
local keys = {}
if nsInfo.name ~= '' then
table.insert(keys, nsInfo.name)
end
if nsInfo.canonicalName ~= nsInfo.name and nsInfo.canonicalName ~= '' then
table.insert(keys, nsInfo.canonicalName)
end
appendMultiple(keys, nsInfo.aliases)
appendMultiple(keys, customAliases)
return keys
end
-- Get the argument for a title's namespace, if it was specified in the args table.
local function getNamespaceArg(title, args)
if title.isTalkPage then
return lookUpNamespaceArg(args, cfg.talk)
end
for _, key in ipairs(getNamespaceArgKeys(title)) do
local arg = lookUpNamespaceArg(args, mw.ustring.lower(key))
if arg ~= nil then
return arg
end
end
return nil
end
-- Look up a page type specific to the title's namespace
local function getExplicitPageType(title)
if title.isTalkPage then
return cfg.talkDefault
else
return cfg.pagetypes[title.namespace]
end
end
-- Get a default page type that is not specific to the title's namespace
local function getDefaultPageType(args)
local other = lookUpNamespaceArg(args, cfg.other)
if type(other) == 'string' then
return other
else
return cfg.otherDefault
end
end
local function detectRedirects(title, args)
local redirect = lookUpNamespaceArg(args, cfg.redirect)
if redirect == false then
-- Don't detect redirects if they have been specifically disallowed.
return nil
end
-- Allow custom values for redirects.
if not title.isRedirect then
return nil
elseif type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
local function capitalize(pageType)
local first = mw.ustring.sub(pageType, 1, 1)
local rest = mw.ustring.sub(pageType, 2)
return mw.ustring.upper(first) .. rest
end
local function pluralize(pageType)
if cfg.irregularPlurals[pageType] then
return cfg.irregularPlurals[pageType]
else
return pageType .. cfg.plural -- often 's'
end
end
local function parseContent(title, args, optionsList)
if title.namespace==828 and title.subpageText~='doc' -- don't detect modules
or not title.exists -- can't check unless page exists
then
return nil
end
local content = title:getContent()
if content == nil then
return nil
end
local templates -- lazily evaluated
for _, options in next, optionsList do
local list, parameter, default, articleOnly = unpack(options, 1, 4)
if not articleOnly or title.namespace==0 then -- only check for templates if we should...
local out = lookUpNamespaceArg(args, parameter)
if type(out) == "string" or (out ~= false and default) then -- ...and if we actually have anything to say about them
if not templates then
templates = {} -- do our delayed evaluation now that we are required to
content = require('Module:Wikitext Parsing').PrepareText(content) -- disregard templates which do not have any affect
for template in string.gmatch(content, "{{%s*([^|}]-)%s*[|}]") do
templates[#templates+1] = capitalize(template)
end
end
local wantedTemplates = mw.loadData('Module:Pagetype/' .. list)
local templateFound = false
for _, template in next, templates do
if wantedTemplates[template] then
templateFound = true
break
end
end
if templateFound then
if type(out)=='string' then
return out
elseif out ~= false and default then
return default
end
end
end
end
end
end
-- Find pages which do not exist
local function nonExistent(title, args)
local arg = lookUpNamespaceArg(args, cfg.ne)
if arg == false then
return nil
end
local exists = false
if title.exists then -- not an article if it does not exist
exists = true
elseif title.namespace==8 and mw.message.new(title.text):exists() then
exists = true
elseif title.namespace==6 and title.fileExists then
exists = true
end
if not exists then
if type(arg) == 'string' then
return arg
else
return cfg.naDefault
end
end
end
-- Get page types for mainspaces pages with an explicit class specified
local function getMainNamespaceClassPageType(title, args)
local class = args[1]
if type(class) == 'string' then -- Put in lower case so e.g. "na" and "NA" will both match
class = mw.ustring.lower(class)
end
local arg = lookUpNamespaceArg(args, cfg.na)
if arg == false then -- don't check for this class if it is specifically disallowed
return nil
end
if cfg.naAliases[class] then
if type(arg) == 'string' then
return arg
else
return cfg.naDefault
end
else
return nil
end
end
-- Get page type specified by an explicit namespace argument.
local function getNamespaceArgPageType(title, args)
local namespaceArg = getNamespaceArg(title, args)
if namespaceArg == true then
-- Namespace has been explicitly enabled, so return the default for
-- this namespace
return getExplicitPageType(title)
elseif namespaceArg == false then
-- Namespace has been explicitly disabled
return getDefaultPageType(args)
elseif namespaceArg then
-- This namespaces uses custom text
return namespaceArg
else
return nil
end
end
-- Get page type not specified or detected by other means
local function getOtherPageType(title, args)
-- Whether the title is in the set of default active namespaces which are looked up in cfg.pagetypes.
local isInDefaultActiveNamespace = false
local defaultNamespacesKey = args[cfg.defaultns]
if defaultNamespacesKey == cfg.defaultnsAll then
isInDefaultActiveNamespace = true
else
local defaultNamespaces
if defaultNamespacesKey == cfg.defaultnsExtended then
defaultNamespaces = cfg.extendedNamespaces
elseif defaultNamespacesKey == cfg.defaultnsNone then
defaultNamespaces = {}
else
defaultNamespaces = cfg.defaultNamespaces
end
isInDefaultActiveNamespace = defaultNamespaces[title.namespace]
end
if isInDefaultActiveNamespace then
return getExplicitPageType(title)
else
return getDefaultPageType(args)
end
end
function p._main(args)
local title
if args.page then
title = mw.title.new(args.page)
else
title = mw.title.getCurrentTitle()
end
if title and not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll then
title = title.subjectPageTitle
end
local pageType = detectRedirects(title, args)
or nonExistent(title, args)
or parseContent(title, args, {
{'softredirect', cfg.softRedirect, cfg.softRedirectDefault},
{'setindex', cfg.sia, cfg.siaDefault, true},
{'disambiguation', cfg.dab, cfg.dabDefault, true},
{'rfd', cfg.rfd, cfg.rfdDefault},
})
or (title.namespace == 0 and getMainNamespaceClassPageType(title, args))
or getNamespaceArgPageType(title, args)
or getOtherPageType(title, args)
if yesno(args.plural, false) then
pageType = pluralize(pageType)
end
if yesno(args.caps, false) then
pageType = capitalize(pageType)
end
return pageType
end
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame)
return p._main(args)
end
return p
nqu0kvn2uc9jqptetkmoluzmahcxzzp
281529
281528
2026-06-07T19:26:19Z
Ameisenigel
44
45 revisions imported from [[:en:Module:Pagetype]]: Request at [[WF:AN]]
281528
Scribunto
text/plain
--------------------------------------------------------------------------------
-- --
-- This meta-module which automatically detects namespaces, and allows --
-- for a great deal of customisation. It can easily be ported to other --
-- wikis by changing the values in the [[Module:Pagetype/config]]. --
-- --
--------------------------------------------------------------------------------
-- Load config.
local cfg = mw.loadData('Module:Pagetype/config')
-- Load required modules.
local yesno = require('Module:Yesno')
local p = {}
-- Look up a namespace argument in the args table.
local function lookUpNamespaceArg(args, key)
local arg = args[key]
-- Convert "yes", "1" etc. to true, "no", "0" etc. to false, and leave
-- other values the same.
return yesno(arg, arg)
end
-- Append multiple values to an array
local function appendMultiple(target, source)
for _, value in ipairs(source) do
table.insert(target, value)
end
end
-- Get argument keys for a title's namespace
local function getNamespaceArgKeys(title)
local nsInfo = mw.site.namespaces[title.namespace]
local customAliases = cfg.customNamespaceAliases[title.namespace] or {}
local keys = {}
if nsInfo.name ~= '' then
table.insert(keys, nsInfo.name)
end
if nsInfo.canonicalName ~= nsInfo.name and nsInfo.canonicalName ~= '' then
table.insert(keys, nsInfo.canonicalName)
end
appendMultiple(keys, nsInfo.aliases)
appendMultiple(keys, customAliases)
return keys
end
-- Get the argument for a title's namespace, if it was specified in the args table.
local function getNamespaceArg(title, args)
if title.isTalkPage then
return lookUpNamespaceArg(args, cfg.talk)
end
for _, key in ipairs(getNamespaceArgKeys(title)) do
local arg = lookUpNamespaceArg(args, mw.ustring.lower(key))
if arg ~= nil then
return arg
end
end
return nil
end
-- Look up a page type specific to the title's namespace
local function getExplicitPageType(title)
if title.isTalkPage then
return cfg.talkDefault
else
return cfg.pagetypes[title.namespace]
end
end
-- Get a default page type that is not specific to the title's namespace
local function getDefaultPageType(args)
local other = lookUpNamespaceArg(args, cfg.other)
if type(other) == 'string' then
return other
else
return cfg.otherDefault
end
end
local function detectRedirects(title, args)
local redirect = lookUpNamespaceArg(args, cfg.redirect)
if redirect == false then
-- Don't detect redirects if they have been specifically disallowed.
return nil
end
-- Allow custom values for redirects.
if not title.isRedirect then
return nil
elseif type(redirect) == 'string' then
return redirect
else
return cfg.redirectDefault
end
end
local function capitalize(pageType)
local first = mw.ustring.sub(pageType, 1, 1)
local rest = mw.ustring.sub(pageType, 2)
return mw.ustring.upper(first) .. rest
end
local function pluralize(pageType)
if cfg.irregularPlurals[pageType] then
return cfg.irregularPlurals[pageType]
else
return pageType .. cfg.plural -- often 's'
end
end
local function parseContent(title, args, optionsList)
if title.namespace==828 and title.subpageText~='doc' -- don't detect modules
or not title.exists -- can't check unless page exists
then
return nil
end
local content = title:getContent()
if content == nil then
return nil
end
local templates -- lazily evaluated
for _, options in next, optionsList do
local list, parameter, default, articleOnly = unpack(options, 1, 4)
if not articleOnly or title.namespace==0 then -- only check for templates if we should...
local out = lookUpNamespaceArg(args, parameter)
if type(out) == "string" or (out ~= false and default) then -- ...and if we actually have anything to say about them
if not templates then
templates = {} -- do our delayed evaluation now that we are required to
content = require('Module:Wikitext Parsing').PrepareText(content) -- disregard templates which do not have any affect
for template in string.gmatch(content, "{{%s*([^|}]-)%s*[|}]") do
templates[#templates+1] = capitalize(template)
end
end
local wantedTemplates = mw.loadData('Module:Pagetype/' .. list)
local templateFound = false
for _, template in next, templates do
if wantedTemplates[template] then
templateFound = true
break
end
end
if templateFound then
if type(out)=='string' then
return out
elseif out ~= false and default then
return default
end
end
end
end
end
end
-- Find pages which do not exist
local function nonExistent(title, args)
local arg = lookUpNamespaceArg(args, cfg.ne)
if arg == false then
return nil
end
local exists = false
if title.exists then -- not an article if it does not exist
exists = true
elseif title.namespace==8 and mw.message.new(title.text):exists() then
exists = true
elseif title.namespace==6 and title.fileExists then
exists = true
end
if not exists then
if type(arg) == 'string' then
return arg
else
return cfg.naDefault
end
end
end
-- Get page types for mainspaces pages with an explicit class specified
local function getMainNamespaceClassPageType(title, args)
local class = args[1]
if type(class) == 'string' then -- Put in lower case so e.g. "na" and "NA" will both match
class = mw.ustring.lower(class)
end
local arg = lookUpNamespaceArg(args, cfg.na)
if arg == false then -- don't check for this class if it is specifically disallowed
return nil
end
if cfg.naAliases[class] then
if type(arg) == 'string' then
return arg
else
return cfg.naDefault
end
else
return nil
end
end
-- Get page type specified by an explicit namespace argument.
local function getNamespaceArgPageType(title, args)
local namespaceArg = getNamespaceArg(title, args)
if namespaceArg == true then
-- Namespace has been explicitly enabled, so return the default for
-- this namespace
return getExplicitPageType(title)
elseif namespaceArg == false then
-- Namespace has been explicitly disabled
return getDefaultPageType(args)
elseif namespaceArg then
-- This namespaces uses custom text
return namespaceArg
else
return nil
end
end
-- Get page type not specified or detected by other means
local function getOtherPageType(title, args)
-- Whether the title is in the set of default active namespaces which are looked up in cfg.pagetypes.
local isInDefaultActiveNamespace = false
local defaultNamespacesKey = args[cfg.defaultns]
if defaultNamespacesKey == cfg.defaultnsAll then
isInDefaultActiveNamespace = true
else
local defaultNamespaces
if defaultNamespacesKey == cfg.defaultnsExtended then
defaultNamespaces = cfg.extendedNamespaces
elseif defaultNamespacesKey == cfg.defaultnsNone then
defaultNamespaces = {}
else
defaultNamespaces = cfg.defaultNamespaces
end
isInDefaultActiveNamespace = defaultNamespaces[title.namespace]
end
if isInDefaultActiveNamespace then
return getExplicitPageType(title)
else
return getDefaultPageType(args)
end
end
function p._main(args)
local title
if args.page then
title = mw.title.new(args.page)
else
title = mw.title.getCurrentTitle()
end
if title and not yesno(args.talk, true) and args[cfg.defaultns] ~= cfg.defaultnsAll then
title = title.subjectPageTitle
end
local pageType = detectRedirects(title, args)
or nonExistent(title, args)
or parseContent(title, args, {
{'softredirect', cfg.softRedirect, cfg.softRedirectDefault},
{'setindex', cfg.sia, cfg.siaDefault, true},
{'disambiguation', cfg.dab, cfg.dabDefault, true},
{'rfd', cfg.rfd, cfg.rfdDefault},
})
or (title.namespace == 0 and getMainNamespaceClassPageType(title, args))
or getNamespaceArgPageType(title, args)
or getOtherPageType(title, args)
if yesno(args.plural, false) then
pageType = pluralize(pageType)
end
if yesno(args.caps, false) then
pageType = capitalize(pageType)
end
return pageType
end
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame)
return p._main(args)
end
return p
nqu0kvn2uc9jqptetkmoluzmahcxzzp
Module:Pagetype/config
828
85276
281530
2014-04-04T04:21:03Z
en>Mr. Stradivarius
0
copy config data from [[Module:Namespace detect]] and format it
281530
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to
-- this table should be namespace parameters that can be used with
-- [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['draft'] = 'draft',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes by default.
cfg.defaultNamespaces = {
'main',
'file',
'template',
'category',
'module',
'book'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
'main',
'user',
'project',
'file',
'mediawiki',
'template',
'category',
'help',
'portal',
'module',
'book',
'draft'
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
'disambiguation',
'disambig',
'disamb',
'dab'
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
d5dmy06qv0sktp18n4y20zfwinn7as9
281531
281530
2014-04-04T05:40:04Z
en>Mr. Stradivarius
0
Protected Module:Pagetype/config: [[Wikipedia:Lua/Modules|High-risk Lua module]] ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite))
281530
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to
-- this table should be namespace parameters that can be used with
-- [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['draft'] = 'draft',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes by default.
cfg.defaultNamespaces = {
'main',
'file',
'template',
'category',
'module',
'book'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
'main',
'user',
'project',
'file',
'mediawiki',
'template',
'category',
'help',
'portal',
'module',
'book',
'draft'
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
'disambiguation',
'disambig',
'disamb',
'dab'
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
d5dmy06qv0sktp18n4y20zfwinn7as9
281532
281531
2014-10-03T13:27:53Z
en>Mr. Stradivarius
0
add Flow boards
281532
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to
-- this table should be namespace parameters that can be used with
-- [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['draft'] = 'draft',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['topic'] = 'topic',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes by default.
cfg.defaultNamespaces = {
'main',
'file',
'template',
'category',
'module',
'book'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
'main',
'user',
'project',
'file',
'mediawiki',
'template',
'category',
'help',
'portal',
'module',
'book',
'draft'
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
'disambiguation',
'disambig',
'disamb',
'dab'
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
4ct6zmhtm1p7mhm9t55efxnids6dn0w
281533
281532
2015-09-28T03:41:25Z
en>Mr. Stradivarius
0
add the gadget and gadget definition namespaces per protected edit request by [[User:GeoffreyT2000]]
281533
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to
-- this table should be namespace parameters that can be used with
-- [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['draft'] = 'draft',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['topic'] = 'topic',
['gadget'] = 'gadget',
['gadget definition'] = 'gadget definition',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file',
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes by default.
cfg.defaultNamespaces = {
'main',
'file',
'template',
'category',
'module',
'book'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
'main',
'user',
'project',
'file',
'mediawiki',
'template',
'category',
'help',
'portal',
'module',
'book',
'draft'
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
'disambiguation',
'disambig',
'disamb',
'dab'
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
nsjvdnxzxja6ehw5fddooj4hmniipbh
281534
281533
2019-04-22T21:09:58Z
en>El C
0
Changed protection level for "[[Module:Pagetype/config]]": [[WP:High-risk templates|High-risk Lua module]] ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite))
281533
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to
-- this table should be namespace parameters that can be used with
-- [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['draft'] = 'draft',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['topic'] = 'topic',
['gadget'] = 'gadget',
['gadget definition'] = 'gadget definition',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file',
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes by default.
cfg.defaultNamespaces = {
'main',
'file',
'template',
'category',
'module',
'book'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
'main',
'user',
'project',
'file',
'mediawiki',
'template',
'category',
'help',
'portal',
'module',
'book',
'draft'
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
'disambiguation',
'disambig',
'disamb',
'dab'
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
nsjvdnxzxja6ehw5fddooj4hmniipbh
281535
281534
2020-06-01T17:52:20Z
en>RexxS
0
update from sandbox
281535
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to
-- this table should be namespace parameters that can be used with
-- [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['draft'] = 'draft',
['education program'] = 'education program page',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['topic'] = 'topic',
['gadget'] = 'gadget',
['gadget definition'] = 'gadget definition',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file',
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes by default.
cfg.defaultNamespaces = {
'main',
'file',
'template',
'category',
'module',
'book'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
'main',
'user',
'project',
'file',
'mediawiki',
'template',
'category',
'help',
'portal',
'module',
'book',
'draft'
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
'disambiguation',
'disambig',
'disamb',
'dab'
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
-- The usual suffix denoting a plural.
cfg.plural = 's'
-- This table holds plurals not formed by a simple suffix.
cfg.irregularPlurals = {
["category"] = "categories"
}
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
tb6svg2he6570yy55mfxge7s1pk60ma
281536
281535
2021-06-21T19:44:00Z
en>Trialpears
0
Education program namespace is no more
281536
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to
-- this table should be namespace parameters that can be used with
-- [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['book'] = 'book',
['draft'] = 'draft',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['topic'] = 'topic',
['gadget'] = 'gadget',
['gadget definition'] = 'gadget definition',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file',
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes by default.
cfg.defaultNamespaces = {
'main',
'file',
'template',
'category',
'module',
'book'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
'main',
'user',
'project',
'file',
'mediawiki',
'template',
'category',
'help',
'portal',
'module',
'book',
'draft'
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
'disambiguation',
'disambig',
'disamb',
'dab'
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
-- The usual suffix denoting a plural.
cfg.plural = 's'
-- This table holds plurals not formed by a simple suffix.
cfg.irregularPlurals = {
["category"] = "categories"
}
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
lv8r4iyvyhbqdoc2xg6czkywezjd1px
281537
281536
2021-07-10T15:47:32Z
en>Trialpears
0
Book namespace removal will happen within a few days
281537
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the values to use for "main=true", "user=true", etc. Keys to
-- this table should be namespace parameters that can be used with
-- [[Module:Namespace detect]].
cfg.pagetypes = {
['main'] = 'article',
['user'] = 'user page',
['project'] = 'project page',
['wikipedia'] = 'project page',
['wp'] = 'project page',
['file'] = 'file',
['image'] = 'file',
['mediawiki'] = 'interface page',
['template'] = 'template',
['help'] = 'help page',
['category'] = 'category',
['portal'] = 'portal',
['draft'] = 'draft',
['timedtext'] = 'Timed Text page',
['module'] = 'module',
['topic'] = 'topic',
['gadget'] = 'gadget',
['gadget definition'] = 'gadget definition',
['talk'] = 'talk page',
['special'] = 'special page',
['media'] = 'file',
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes by default.
cfg.defaultNamespaces = {
'main',
'file',
'template',
'category',
'module'
}
-- This table holds the names of the namespaces to be looked up from
-- cfg.pagetypes if cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
'main',
'user',
'project',
'file',
'mediawiki',
'template',
'category',
'help',
'portal',
'module',
'draft'
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
'disambiguation',
'disambig',
'disamb',
'dab'
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {'na', 'n/a'}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
-- The usual suffix denoting a plural.
cfg.plural = 's'
-- This table holds plurals not formed by a simple suffix.
cfg.irregularPlurals = {
["category"] = "categories"
}
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
qi8m5o0e66j9quw6kiawd2otsulzq9m
281538
281537
2023-08-28T13:36:34Z
en>Mr. Stradivarius
0
update from [[Module:Pagetype/config/sandbox]]
281538
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the default page types for each namespace. Keys to this
-- table should be integers that can be used as keys to mw.site.namespaces.
cfg.pagetypes = {
[0] = 'article', -- Main namespace
[2] = 'user page',
[4] = 'project page',
[6] = 'file',
[8] = 'interface page', -- MediaWiki namespace
[10] = 'template',
[12] = 'help page',
[14] = 'category',
[100] = 'portal',
[118] = 'draft',
[710] = 'Timed Text page',
[828] = 'module',
[2300] = 'gadget',
[2302] = 'gadget definition',
[-1] = 'special page',
[-2] = 'file', -- Media namespace
}
-- This table holds the namespaces to be looked up from cfg.pagetypes by
-- default.
cfg.defaultNamespaces = {
[0] = true, -- main
[6] = true, -- file
[10] = true, -- template
[14] = true, -- category
[828] = true, -- module
}
-- This table holds the namespaces to be looked up from cfg.pagetypes if
-- cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
[0] = true, -- main
[2] = true, -- user
[4] = true, -- project
[6] = true, -- file
[8] = true, -- mediawiki
[10] = true, -- template
[12] = true, -- help
[14] = true, -- category
[100] = true, -- portal
[118] = true, -- draft
[828] = true, -- module
}
-- This table holds custom aliases for each namespace.
cfg.customNamespaceAliases = {
[0] = {'main'},
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for talk pages.
cfg.talk = 'talk'
-- The default value for talk pages.
cfg.talkDefault = 'talk page'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
['disambiguation'] = true,
['disambig'] = true,
['disamb'] = true,
['dab'] = true,
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {
['na'] = true,
['n/a'] = true,
}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
-- The usual suffix denoting a plural.
cfg.plural = 's'
-- This table holds plurals not formed by a simple suffix.
cfg.irregularPlurals = {
["category"] = "categories"
}
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
9qmc982q65l2h7aj1wblije3j52xkbx
281539
281538
2024-01-31T19:43:55Z
en>MSGJ
0
add cfg for soft redirects and SIAs
281539
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the default page types for each namespace. Keys to this
-- table should be integers that can be used as keys to mw.site.namespaces.
cfg.pagetypes = {
[0] = 'article', -- Main namespace
[2] = 'user page',
[4] = 'project page',
[6] = 'file',
[8] = 'interface page', -- MediaWiki namespace
[10] = 'template',
[12] = 'help page',
[14] = 'category',
[100] = 'portal',
[118] = 'draft',
[710] = 'Timed Text page',
[828] = 'module',
[2300] = 'gadget',
[2302] = 'gadget definition',
[-1] = 'special page',
[-2] = 'file', -- Media namespace
}
-- This table holds the namespaces to be looked up from cfg.pagetypes by
-- default.
cfg.defaultNamespaces = {
[0] = true, -- main
[6] = true, -- file
[10] = true, -- template
[14] = true, -- category
[828] = true, -- module
}
-- This table holds the namespaces to be looked up from cfg.pagetypes if
-- cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
[0] = true, -- main
[2] = true, -- user
[4] = true, -- project
[6] = true, -- file
[8] = true, -- mediawiki
[10] = true, -- template
[12] = true, -- help
[14] = true, -- category
[100] = true, -- portal
[118] = true, -- draft
[828] = true, -- module
}
-- This table holds custom aliases for each namespace.
cfg.customNamespaceAliases = {
[0] = {'main'},
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for talk pages.
cfg.talk = 'talk'
-- The default value for talk pages.
cfg.talkDefault = 'talk page'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
cfg.softRedirect = 'soft_redirect'
cfg.softRedirectDefault = 'redirect'
cfg.sia = 'sia'
cfg.siaDefault = 'article'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
['disambiguation'] = true,
['disambig'] = true,
['disamb'] = true,
['dab'] = true,
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {
['na'] = true,
['n/a'] = true,
}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
-- The usual suffix denoting a plural.
cfg.plural = 's'
-- This table holds plurals not formed by a simple suffix.
cfg.irregularPlurals = {
["category"] = "categories"
}
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
skpulsais2cx5v5l6zs9kcycf2ezrmi
281540
281539
2024-05-09T08:50:37Z
en>MSGJ
0
add rfd
281540
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the default page types for each namespace. Keys to this
-- table should be integers that can be used as keys to mw.site.namespaces.
cfg.pagetypes = {
[0] = 'article', -- Main namespace
[2] = 'user page',
[4] = 'project page',
[6] = 'file',
[8] = 'interface page', -- MediaWiki namespace
[10] = 'template',
[12] = 'help page',
[14] = 'category',
[100] = 'portal',
[118] = 'draft',
[710] = 'Timed Text page',
[828] = 'module',
[2300] = 'gadget',
[2302] = 'gadget definition',
[-1] = 'special page',
[-2] = 'file', -- Media namespace
}
-- This table holds the namespaces to be looked up from cfg.pagetypes by
-- default.
cfg.defaultNamespaces = {
[0] = true, -- main
[6] = true, -- file
[10] = true, -- template
[14] = true, -- category
[828] = true, -- module
}
-- This table holds the namespaces to be looked up from cfg.pagetypes if
-- cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
[0] = true, -- main
[2] = true, -- user
[4] = true, -- project
[6] = true, -- file
[8] = true, -- mediawiki
[10] = true, -- template
[12] = true, -- help
[14] = true, -- category
[100] = true, -- portal
[118] = true, -- draft
[828] = true, -- module
}
-- This table holds custom aliases for each namespace.
cfg.customNamespaceAliases = {
[0] = {'main'},
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for talk pages.
cfg.talk = 'talk'
-- The default value for talk pages.
cfg.talkDefault = 'talk page'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
cfg.softRedirect = 'soft_redirect'
cfg.softRedirectDefault = 'redirect'
cfg.sia = 'sia'
cfg.siaDefault = 'article'
cfg.rfd = 'redirect'
cfg.rfdDefault = 'redirect'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
['disambiguation'] = true,
['disambig'] = true,
['disamb'] = true,
['dab'] = true,
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {
['na'] = true,
['n/a'] = true,
}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
-- The usual suffix denoting a plural.
cfg.plural = 's'
-- This table holds plurals not formed by a simple suffix.
cfg.irregularPlurals = {
["category"] = "categories"
}
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
b11w63qzip05yx5s26i15jk97d3x4s8
281541
281540
2024-05-16T12:23:09Z
en>MSGJ
0
+cfg for non-existent pages
281541
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the default page types for each namespace. Keys to this
-- table should be integers that can be used as keys to mw.site.namespaces.
cfg.pagetypes = {
[0] = 'article', -- Main namespace
[2] = 'user page',
[4] = 'project page',
[6] = 'file',
[8] = 'interface page', -- MediaWiki namespace
[10] = 'template',
[12] = 'help page',
[14] = 'category',
[100] = 'portal',
[118] = 'draft',
[710] = 'Timed Text page',
[828] = 'module',
[2300] = 'gadget',
[2302] = 'gadget definition',
[-1] = 'special page',
[-2] = 'file', -- Media namespace
}
-- This table holds the namespaces to be looked up from cfg.pagetypes by
-- default.
cfg.defaultNamespaces = {
[0] = true, -- main
[6] = true, -- file
[10] = true, -- template
[14] = true, -- category
[828] = true, -- module
}
-- This table holds the namespaces to be looked up from cfg.pagetypes if
-- cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
[0] = true, -- main
[2] = true, -- user
[4] = true, -- project
[6] = true, -- file
[8] = true, -- mediawiki
[10] = true, -- template
[12] = true, -- help
[14] = true, -- category
[100] = true, -- portal
[118] = true, -- draft
[828] = true, -- module
}
-- This table holds custom aliases for each namespace.
cfg.customNamespaceAliases = {
[0] = {'main'},
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for talk pages.
cfg.talk = 'talk'
-- The default value for talk pages.
cfg.talkDefault = 'talk page'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- The parameter name to use for non-existent pages.
cfg.ne = 'nonexistent'
cfg.neDefault = 'page'
cfg.softRedirect = 'soft_redirect'
cfg.softRedirectDefault = 'redirect'
cfg.sia = 'sia'
cfg.siaDefault = 'article'
cfg.rfd = 'redirect'
cfg.rfdDefault = 'redirect'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
['disambiguation'] = true,
['disambig'] = true,
['disamb'] = true,
['dab'] = true,
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {
['na'] = true,
['n/a'] = true,
}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
-- The usual suffix denoting a plural.
cfg.plural = 's'
-- This table holds plurals not formed by a simple suffix.
cfg.irregularPlurals = {
["category"] = "categories"
}
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
61h7tsfs7sjoixfi1zfjharglkpqbji
281542
281541
2026-04-25T00:17:56Z
en>Anomie
0
+ MOS and Event namespaces
281542
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the default page types for each namespace. Keys to this
-- table should be integers that can be used as keys to mw.site.namespaces.
cfg.pagetypes = {
[0] = 'article', -- Main namespace
[2] = 'user page',
[4] = 'project page',
[6] = 'file',
[8] = 'interface page', -- MediaWiki namespace
[10] = 'template',
[12] = 'help page',
[14] = 'category',
[100] = 'portal',
[118] = 'draft',
[126] = 'MOS page',
[710] = 'Timed Text page',
[828] = 'module',
[1728] = 'event page',
[2300] = 'gadget',
[2302] = 'gadget definition',
[-1] = 'special page',
[-2] = 'file', -- Media namespace
}
-- This table holds the namespaces to be looked up from cfg.pagetypes by
-- default.
cfg.defaultNamespaces = {
[0] = true, -- main
[6] = true, -- file
[10] = true, -- template
[14] = true, -- category
[828] = true, -- module
}
-- This table holds the namespaces to be looked up from cfg.pagetypes if
-- cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
[0] = true, -- main
[2] = true, -- user
[4] = true, -- project
[6] = true, -- file
[8] = true, -- mediawiki
[10] = true, -- template
[12] = true, -- help
[14] = true, -- category
[100] = true, -- portal
[118] = true, -- draft
[828] = true, -- module
}
-- This table holds custom aliases for each namespace.
cfg.customNamespaceAliases = {
[0] = {'main'},
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for talk pages.
cfg.talk = 'talk'
-- The default value for talk pages.
cfg.talkDefault = 'talk page'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- The parameter name to use for non-existent pages.
cfg.ne = 'nonexistent'
cfg.neDefault = 'page'
cfg.softRedirect = 'soft_redirect'
cfg.softRedirectDefault = 'redirect'
cfg.sia = 'sia'
cfg.siaDefault = 'article'
cfg.rfd = 'redirect'
cfg.rfdDefault = 'redirect'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
['disambiguation'] = true,
['disambig'] = true,
['disamb'] = true,
['dab'] = true,
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {
['na'] = true,
['n/a'] = true,
}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
-- The usual suffix denoting a plural.
cfg.plural = 's'
-- This table holds plurals not formed by a simple suffix.
cfg.irregularPlurals = {
["category"] = "categories"
}
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
dbot8rq8sjl2bypo7kiymiyo4gxz08i
281543
281542
2026-06-07T19:27:44Z
Ameisenigel
44
13 revisions imported from [[:en:Module:Pagetype/config]]: Request at [[WF:AN]]
281542
Scribunto
text/plain
--------------------------------------------------------------------------------
-- Module:Pagetype configuration data --
-- This page holds localisation and configuration data for Module:Pagetype. --
--------------------------------------------------------------------------------
local cfg = {} -- Don't edit this line.
--------------------------------------------------------------------------------
-- Start configuration data --
--------------------------------------------------------------------------------
-- This table holds the default page types for each namespace. Keys to this
-- table should be integers that can be used as keys to mw.site.namespaces.
cfg.pagetypes = {
[0] = 'article', -- Main namespace
[2] = 'user page',
[4] = 'project page',
[6] = 'file',
[8] = 'interface page', -- MediaWiki namespace
[10] = 'template',
[12] = 'help page',
[14] = 'category',
[100] = 'portal',
[118] = 'draft',
[126] = 'MOS page',
[710] = 'Timed Text page',
[828] = 'module',
[1728] = 'event page',
[2300] = 'gadget',
[2302] = 'gadget definition',
[-1] = 'special page',
[-2] = 'file', -- Media namespace
}
-- This table holds the namespaces to be looked up from cfg.pagetypes by
-- default.
cfg.defaultNamespaces = {
[0] = true, -- main
[6] = true, -- file
[10] = true, -- template
[14] = true, -- category
[828] = true, -- module
}
-- This table holds the namespaces to be looked up from cfg.pagetypes if
-- cfg.defaultnsExtended is set.
cfg.extendedNamespaces = {
[0] = true, -- main
[2] = true, -- user
[4] = true, -- project
[6] = true, -- file
[8] = true, -- mediawiki
[10] = true, -- template
[12] = true, -- help
[14] = true, -- category
[100] = true, -- portal
[118] = true, -- draft
[828] = true, -- module
}
-- This table holds custom aliases for each namespace.
cfg.customNamespaceAliases = {
[0] = {'main'},
}
-- The parameter name to set which default namespace values to be looked up from
-- cfg.pagetypes.
cfg.defaultns = 'defaultns'
-- The value of cfg.defaultns to set all namespaces, including talk.
cfg.defaultnsAll = 'all'
-- The value of cfg.defaultns to set the namespaces listed in
-- cfg.extendedNamespaces
cfg.defaultnsExtended = 'extended'
-- The value of cfg.defaultns to set no default namespaces.
cfg.defaultnsNone = 'none'
-- The parameter name to use for talk pages.
cfg.talk = 'talk'
-- The default value for talk pages.
cfg.talkDefault = 'talk page'
-- The parameter name to use for disambiguation pages page.
cfg.dab = 'dab'
-- The parameter name to use for non-existent pages.
cfg.ne = 'nonexistent'
cfg.neDefault = 'page'
cfg.softRedirect = 'soft_redirect'
cfg.softRedirectDefault = 'redirect'
cfg.sia = 'sia'
cfg.siaDefault = 'article'
cfg.rfd = 'redirect'
cfg.rfdDefault = 'redirect'
-- This table holds the different possible aliases for disambiguation-class
-- pages. These should be lower-case.
cfg.dabAliases = {
['disambiguation'] = true,
['disambig'] = true,
['disamb'] = true,
['dab'] = true,
}
-- The default value for disambiguation pages.
cfg.dabDefault = 'page'
-- The parameter name to use for N/A-class page.
cfg.na = 'na'
-- This table holds the different possible aliases for N/A-class pages. These
-- should be lower-case.
cfg.naAliases = {
['na'] = true,
['n/a'] = true,
}
-- The default value for N/A-class pages.
cfg.naDefault = 'page'
-- The parameter name to use for redirects.
cfg.redirect = 'redirect'
-- The default value to use for redirects.
cfg.redirectDefault = 'redirect'
-- The parameter name for undefined namespaces.
cfg.other = 'other'
-- The value used if the module detects an undefined namespace.
cfg.otherDefault = 'page'
-- The usual suffix denoting a plural.
cfg.plural = 's'
-- This table holds plurals not formed by a simple suffix.
cfg.irregularPlurals = {
["category"] = "categories"
}
--------------------------------------------------------------------------------
-- End configuration data --
--------------------------------------------------------------------------------
return cfg -- Don't edit this line
dbot8rq8sjl2bypo7kiymiyo4gxz08i
Module:Wikitext Parsing
828
85277
281544
2023-03-08T14:06:32Z
en>Aidan9382
0
very barebones but should become something
281544
Scribunto
text/plain
local p = {}
local function startswith(text, subtext) --Helper function
return string.sub(text, 1, #subtext) == subtext
end
local TextObject = {}
local function NewTextObject(text)
return setmetatable({text=text},{
__index = function(self, key)
return TextObject[key]
end
})
end
function TextObject:Advance(n)
self.text = string.sub(self.text, (n or 1)+1, -1)
end
function TextObject:GetTagPair(tagname)
tagname = tagname:lower()
if startswith(self.text:lower(), "<"..tagname) then
return true
end
return false
end
p.NewTextObject = NewTextObject
local function main(...)
return "Not done..."
end
return p
ectly2ogd54w99m4tynmwqko5ur28fv
281545
281544
2023-03-09T08:21:36Z
en>Aidan9382
0
still in progress
281545
Scribunto
text/plain
local p = {}
local function startswith(text, subtext) --Helper function
return string.sub(text, 1, #subtext) == subtext
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight) ----
SIDENOTE: <pre> functions like a normal tag, so the rules from above apply
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local function TestForNowikiTag(text)
local validtags = {"nowiki", "pre", "syntaxhighlight"}
if startswith(text, "<") then
local tagName = string.match(text:lower(),"^<([^\n />]+)")
if tagName and table.find(validtags, tagName) then
local nextOpener = string.find(text,"<",2) or -1
local nextCloser = string.find(text,">",2) or -1
if nextOpener < nextCloser and nextCloser > -1 then
local StartingTag = string.sub(text, 1, nextCloser)
end
end
end
return nil
end
local function TestForComment() --Like GetTagToken but specialised
end
local function EscapeEscapedText(text) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<")
--Advance to the next potential tag we care about
end
return newtext
end
p.EET = EscapeEscapedText
function p.main(...)
return "Not done..."
end
return p
iyxepgvni367dppd9ogmncpzwu9u2lg
281546
281545
2023-03-09T09:07:49Z
en>Aidan9382
0
stuff
281546
Scribunto
text/plain
local p = {}
local function startswith(text, subtext) --Helper function
return string.sub(text, 1, #subtext) == subtext
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight) ----
SIDENOTE: <pre> functions like a normal tag, so the rules from above apply
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local function TestForNowikiTag(text)
local validtags = {"nowiki", "pre", "syntaxhighlight"}
if startswith(text, "<") then
local tagName = string.match(text:lower(),"^<([^\n />]+)")
if tagName and table.find(validtags, tagName) then
local nextOpener = string.find(text,"<",2) or -1
local nextCloser = string.find(text,">",2) or -1
if nextOpener < nextCloser and nextCloser > -1 then
local StartingTag = string.sub(text, 1, nextCloser)
end
end
end
return nil
end
local function TestForComment(text) --Like GetTagToken but specialised
end
local function EscapeEscapedText(text) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<") --Advance to the next potential tag we care about
if not NextCheck then
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
else
local NowikiTag = TestForNowikiTag(text)
end
end
return newtext
end
p.EET = EscapeEscapedText
function p.main(...)
return "Not done..."
end
return p
--[[ console
local out = p.EET([=[Hey!{{Text|<nowiki>
Test}}
</nowiki>}}]=])
mw.log(out)
]]
p7spgevmehs8n08weljoc7mz0jia3tc
281547
281546
2023-03-09T11:03:46Z
en>Aidan9382
0
done for tags/comments
281547
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function intable(t, v)
for a,b in next,t do
if b == v then
return true
end
end
return false
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: <pre> functions like a normal tag, so the rules from above apply
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local validtags = {"nowiki", "pre", "syntaxhighlight", "source"}
if startswith(text, "<") then
local tagName = string.match(text:lower(),"^<([^\n />]+)")
if tagName and intable(validtags, tagName) then
local nextOpener = string.find(text,"<",2) or -1
local nextCloser = string.find(text,">",2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
mw.log(startingTag,"is self closing")
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
if tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text:lower(), "</pre>") or
string.match(text:lower(), "</pre[ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text:lower(), endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text:lower(), "</"..tagName.."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text:lower(), endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else
mw.log("Found starting tag",startingTag,"but nothing to finish it off in",text)
return nil
end
end
else --if closer/openers are invalid
mw.log("Poor closers!",nextOpener,nextCloser)
return nil
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but specialised
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true) or #text+1
return {
Content = string.sub(text, 5, commentEnd-1)
}
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using EscapeEscapedTextV2 will attempt to "adapt" the text. Comments will be
removed from the final output, and only the content of noprocessing tags will
be left in the final result, not the tags themselves.
--]]
local function EscapeEscapedText(text) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
else
local NowikiTag = TestForNowikiTag(text)
if NowikiTag then
else
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
local function EscapeEscapedTextV2(text) --What a name!
return "Haven't even done V1 yet"
end
p.EET = EscapeEscapedText
p.EETV2 = EscapeEscapedTextV2
p.TFNWT = TestForNowikiTag
p.TFC = TestForComment
function p.main(...)
return "Not done..."
end
return p
--[[ console
local out = p.EET([=[Hey!{{Text|<nowiki>
Test}}
</nowiki>}}]=])
mw.log(out)
]]
g3x625fua0od1r47e2lug71b2fvgxq7
281548
281547
2023-03-09T11:34:37Z
en>Aidan9382
0
EET and EETV2 done
281548
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function intable(t, v)
for a,b in next,t do
if b == v then
return true
end
end
return false
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local validtags = {"nowiki", "pre", "syntaxhighlight", "source"}
if startswith(text, "<") then
local tagName = string.match(text:lower(),"^<([^\n />]+)")
if tagName and intable(validtags, tagName) then
local nextOpener = string.find(text,"<",2) or -1
local nextCloser = string.find(text,">",2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text:lower(), "</pre>") or
string.match(text:lower(), "</pre[ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text:lower(), endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text:lower(), "</"..tagName.."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text:lower(), endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but specialised
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using EscapeEscapedTextV2 will attempt to "adapt" the text. Comments will be
removed from the final output, and only the content of noprocessing tags will
be left in the final result, not the tags themselves.
--]]
local function EscapeEscapedText(text) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
local function EscapeEscapedTextV2(text)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
--Leave it out, we don't need it
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newContent --Remove the tags, don't want em
--(NOTE: Consider keeping syntaxhighlight/pre for the formatting, idk)
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
p.EET = EscapeEscapedText
p.EETV2 = EscapeEscapedTextV2
p.TFNWT = TestForNowikiTag
p.TFC = TestForComment
function p.main(...)
return "Not done..."
end
return p
--[[ console
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
l2xl4oezysmsl9pz3t411flsyy2w6kj
281549
281548
2023-03-09T16:37:00Z
en>Aidan9382
0
turns out the system processes in a simpler way, so here's an alternative and much simpler approach
281549
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function intable(t, v)
for a,b in next,t do
if b == v then
return true
end
end
return false
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local validtags = {"nowiki", "pre", "syntaxhighlight", "source"}
if startswith(text, "<") then
local tagName = string.match(text:lower(),"^<([^\n />]+)")
if tagName and intable(validtags, tagName) then
local nextOpener = string.find(text,"<",2) or -1
local nextCloser = string.find(text,">",2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text:lower(), "</pre>") or
string.match(text:lower(), "</pre[ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text:lower(), endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text:lower(), "</"..tagName.."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text:lower(), endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but specialised
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
p.EET = EscapeEscapedText
p.TFNWT = TestForNowikiTag
p.TFC = TestForComment
function p.main(...)
return "Not done..."
end
return p
--[[ console
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
ewechmw926jam53iswa5wilsogcc8j0
281550
281549
2023-03-09T19:59:52Z
en>Aidan9382
0
minor stuff
281550
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function intable(t, v)
for a,b in next,t do
if b == v then
return true
end
end
return false
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local validtags = {"nowiki", "pre", "syntaxhighlight", "source"}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
if startswith(text, "<") then
local tagName = string.match(text:lower(), "^<([^\n />]+)")
if tagName and intable(validtags, tagName) then
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text:lower(), "</pre>") or
string.match(text:lower(), "</pre[ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text:lower(), endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text:lower(), "</"..tagName.."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text:lower(), endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but specialised
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
p.TFNWT = TestForNowikiTag
p.TFC = TestForComment
p.EET = EscapeEscapedText
function p.main(...)
return "<span class='error'>See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
end
return p
--[[ console
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
9ymn0ytfzbgbydvlv656yyf6roxecxu
281551
281550
2023-03-16T16:37:42Z
en>Aidan9382
0
calling text:lower() this many times is very expensive
281551
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function intable(t, v)
for a,b in next,t do
if b == v then
return true
end
end
return false
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local validtags = {"nowiki", "pre", "syntaxhighlight", "source"}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
if startswith(text, "<") then
local textlower = text:lower()
local tagName = string.match(textlower, "^<([^\n />]+)")
if tagName and intable(validtags, tagName) then
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(textlower, "</pre>") or
string.match(textlower, "</pre[ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(textlower, endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(textlower, "</"..tagName.."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(textlower, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but specialised
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
p.TFNWT = TestForNowikiTag
p.TFC = TestForComment
p.EET = EscapeEscapedText
function p.main(...)
return "<span class='error'>See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
end
return p
--[[ console
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
2thce5u8qubexaq22fw93yvavt4q3pr
281552
281551
2023-03-16T16:47:24Z
en>Aidan9382
0
actually, lets be smarter about this and not do lower on entire strings when we just don't have to (or preferably dont use lower at all)
281552
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function intable(t, v)
for a,b in next,t do
if b == v then
return true
end
end
return false
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local validtags = {"nowiki", "pre", "syntaxhighlight", "source"}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
if startswith(text, "<") then
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if intable(validtags, tagName) then
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text, endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
p.TFNWT = TestForNowikiTag
p.TFC = TestForComment
p.EET = EscapeEscapedText
function p.main(...)
return "<span class='error'>See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
end
return p
--[[ console
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
358ybvwqvex1qt0ljwoqfo3d0vr2r0r
281553
281552
2023-03-16T16:50:04Z
en>Aidan9382
0
remove excessive startswith check for TFNWT
281553
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function intable(t, v)
for a,b in next,t do
if b == v then
return true
end
end
return false
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local validtags = {"nowiki", "pre", "syntaxhighlight", "source"}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if intable(validtags, tagName) then
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text, endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
p.TFNWT = TestForNowikiTag
p.TFC = TestForComment
p.EET = EscapeEscapedText
function p.main(...)
return "<span class='error'>See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
end
return p
--[[ console
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
dqg3j7a6bcvkuoqxcljeb7pac1hnnc6
281554
281553
2023-03-16T16:52:40Z
en>Aidan9382
0
remove intable, dedent everything
281554
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text, endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
p.TFNWT = TestForNowikiTag
p.TFC = TestForComment
p.EET = EscapeEscapedText
function p.main(...)
return "<span class='error'>See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
end
return p
--[[ console
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
l7hs1o0pl0tthhiaukqaikfyn5dotfv
281555
281554
2023-03-16T18:42:20Z
en>Aidan9382
0
potential optimisation by avoiding doing too many tests
281555
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text, endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPp!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
p.TFNWT = TestForNowikiTag
p.TFC = TestForComment
p.EET = EscapeEscapedText
function p.main(...)
return "<span class='error'>See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
end
return p
--[[ console
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
1cja0tcubv1c9z8eitfwgdfjv7os13x
281556
281555
2023-03-16T19:11:14Z
en>Aidan9382
0
give it some proper entry points
281556
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text, endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPp!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--Main entry points
p.EscapeEscapedText = EscapeEscapedText
p.EET = EscapeEscapedText
function p.main(o1, o2)
if type(o1) == "table" and o1.getParent then --Template invocation most likely
--return "<span class='error'>See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
o1, o2 = o1.args[1], o1.args[2] --Gonna see how this plays out
return EscapeEscapedText(o1, o2)
else --Module invocation, defer to EET
return EscapeEscapedText(o1, o2)
end
end
--Extra entry points, likely not required
p.TestForNowikiTag = TestForNowikiTag
p.TFNWT = TestForNowikiTag
p.TestForComment = TestForComment
p.TTC = TestForComment
return p
--[[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
hfird18r2umlyvz3nln8h5t86lffff8
281557
281556
2023-03-16T19:12:41Z
en>Aidan9382
0
yeah template invocation is a no
281557
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text, endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPp!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--Main entry points
p.EscapeEscapedText = EscapeEscapedText
p.EET = EscapeEscapedText
function p.main(o1, o2)
if type(o1) == "table" and o1.getParent then --Template invocation most likely
return "<span class='error'>See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
else --Module invocation, defer to EET
return EscapeEscapedText(o1, o2)
end
end
--Extra entry points, likely not required
p.TestForNowikiTag = TestForNowikiTag
p.TFNWT = TestForNowikiTag
p.TestForComment = TestForComment
p.TTC = TestForComment
return p
--[[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
l7pip2by7si6hpmv1r6ls7ile3nhmtb
281558
281557
2023-03-16T19:14:03Z
en>Aidan9382
0
better message
281558
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe] with no room for whitespace, but may
then flow as they want afterwards, making <div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at the first \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
SIDENOTE: For this specific paragraph, <pre> functions like a normal tag, so the
rules from above apply instead.
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice
When something in the code is referenced to as a "NowikiTag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
Since we only care about these 3 tags, we ignore the idea of an intercepting tag
preventing processing, and just go straight for the first ending we can find
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
elseif tagName == "pre" then
local endingTagPosition = #text+1 --<pre> consumes all text if unended
local endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>") or ""
if endingTag ~= "" then
endingTagPosition = string.find(text, endingTag, nextCloser, true)
end
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --<nowiki> and <syntaxhighlight> are stricter
local endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
if endingTag then
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPp!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --if no end tag, keep it that way
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--Main entry points
p.EscapeEscapedText = EscapeEscapedText
p.EET = EscapeEscapedText
function p.main(o1, o2)
if type(o1) == "table" and o1.getParent then --Template invocation most likely
return "<span class='error'>This module can't be invoked directly via a Template. " ..
"See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
else --Module invocation, defer to EET
return EscapeEscapedText(o1, o2)
end
end
--Extra entry points, likely not required
p.TestForNowikiTag = TestForNowikiTag
p.TFNWT = TestForNowikiTag
p.TestForComment = TestForComment
p.TTC = TestForComment
return p
--[[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
hy29ml893ncpyk4ge44mdgwn9fpa29y
281559
281558
2023-03-17T06:56:51Z
en>Aidan9382
0
Behaviour correction (pre does NOT consume all text when it comes to processing if there is no end - it's only visually that it consumes all text, which is confusing)
281559
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
---- HTML COMMENT ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (anything in nowiki, pre, syntaxhighlight, or <!----> tags).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPp!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--Main entry points
p.EscapeEscapedText = EscapeEscapedText
p.EET = EscapeEscapedText
function p.main(o1, o2)
if type(o1) == "table" and o1.getParent then --Template invocation most likely
return "<span class='error'>This module can't be invoked directly via a Template. " ..
"See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
else --Module invocation, defer to EET
return EscapeEscapedText(o1, o2)
end
end
--Extra entry points, likely not required
p.TestForNowikiTag = TestForNowikiTag
p.TFNWT = TestForNowikiTag
p.TestForComment = TestForComment
p.TTC = TestForComment
return p
--[[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
qfq6atheyl449rk40ucr5gt21svw4au
281560
281559
2023-03-17T10:41:23Z
en>Aidan9382
0
work on includeonly tag behaviour
281560
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything.
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, includeonly=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
elseif tagName == "includeonly" then --Doesn't require an ending
local tagContent = string.sub(text, nextCloser+1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = "", Length = #startingTag + #tagContent --#text
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPpIi!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--Main entry points
p.EscapeEscapedText = EscapeEscapedText
p.EET = EscapeEscapedText
function p.main(o1, o2)
if type(o1) == "table" and o1.getParent then --Template invocation most likely
return "<span class='error'>This module can't be invoked directly via a Template. " ..
"See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
else --Module invocation, defer to EET
return EscapeEscapedText(o1, o2)
end
end
--Extra entry points, likely not required
p.TestForNowikiTag = TestForNowikiTag
p.TFNWT = TestForNowikiTag
p.TestForComment = TestForComment
p.TTC = TestForComment
return p
--[[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
g04pa56dgu5u3a5iujgnw9f5yp604cu
281561
281560
2023-03-17T11:02:13Z
en>Aidan9382
0
on second thought, ignoring includeonly tags
281561
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function EscapeEscapedText(text, keepComments) --What a name!
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPp!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--Main entry points
p.EscapeEscapedText = EscapeEscapedText
p.EET = EscapeEscapedText
function p.main(o1, o2)
if type(o1) == "table" and o1.getParent then --Template invocation most likely
return "<span class='error'>This module can't be invoked directly via a Template. " ..
"See [[Module:Sandbox/Aidan9382/ExcessiveParsing/doc]] for proper usage of this module</span>"
else --Module invocation, defer to EET
return EscapeEscapedText(o1, o2)
end
end
--Extra entry points, likely not required
p.TestForNowikiTag = TestForNowikiTag
p.TFNWT = TestForNowikiTag
p.TestForComment = TestForComment
p.TTC = TestForComment
return p
--[[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.EET(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TFC(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
fabxm1nnwnbapt2uohgg98n229gw88y
281562
281561
2023-03-17T12:36:08Z
en>Aidan9382
0
start on template parsing, remove .main as an entry point, change EscapeEscapedText to PrepareText
281562
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPp!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
The trouble here is when the amount of opening and closing braces don't match
E.g.
{{{!}} -> { {{!}} -> {|
{{{{{A}}}} -> {{ {{{A}}} }
The rough idea is to match based off of the smallest amount of braces from the
two sides and treat the excess as regular text.
Templates beginning with the characters listed
[[Wikipedia:Page name#Technical restrictions and limitations|here]] won't be
considered as valid syntax inside {{ }}
Since we can't account for literally every scenario without it getting insane
very fast, syntax like {{{{{A}}}}} will be considered invalid, claiming {{{A}}}
is an invalid name regardless of what it resolves to. This means we don't have
to consider some complications. While this obviously isn't perfect, it's the
best approach I can form right now.
Setting dontEscape will prevent running the input text through EET. Only do this
if it's already been passed through manually.
--]=]
local function ParseTemplates(text, dontEscape) --Need to think about how the execution logic on this is gonna work, so its not done yet
if not dontEscape then
text = PrepareText(text)
end
local templates = {}
local templateData = {}
while text ~= "" do
local NextTemplate = string.find(text,"{{") --Skip to next notable block
if not NextTemplate then --Done
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
end
return templates, templateData
end
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, likely not required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
]]
4q7jxmbvyf2ofmgdyyrrciaucsk7qyl
281563
281562
2023-03-18T09:20:48Z
en>Aidan9382
0
wikilinks done, now onto the worse one
281563
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPp!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes (This needs re-doing pending new changes...........)
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
The trouble here is when the amount of opening and closing braces don't match
E.g.
{{{!}} -> { {{!}} -> {|
{{{{{A}}}} -> {{ {{{A}}} }
The rough idea is to match based off of the smallest amount of braces from the
two sides and treat the excess as regular text.
Templates beginning with the characters listed
[[Wikipedia:Page name#Technical restrictions and limitations|here]] won't be
considered as valid syntax inside {{ }}
Since we can't account for literally every scenario without it getting insane
very fast, syntax like {{{{{A}}}}} will be considered invalid, claiming {{{A}}}
is an invalid name regardless of what it resolves to. This means we don't have
to consider some complications. While this obviously isn't perfect, it's the
best approach I can form right now.
Setting dontEscape will prevent running the input text through EET. Only do this
if it's already been passed through manually.
--]=]
local function ParseTemplates(text, dontEscape) --Need to think about how the execution logic on this is gonna work, so its not done yet
if not dontEscape then
text = PrepareText(text)
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while text ~= "" do
local NextOpen = string.find(text,"%[%[", scannerPosition) or 9e9
local NextClose = string.find(text,"]]", scannerPosition) or 9e9
--mw.log("Debug time | Step 1")
--mw.log("NO, NC =", NextOpen, ",", NextClose)
--mw.logObject(wikilinks)
--mw.logObject(openWikilinks)
if NextOpen == NextClose then --Done (both 9e9)
break
end
local NextCheck = math.min(NextOpen or 9e9, NextClose or 9e9) --Skip to next notable block
scannerPosition = NextCheck+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
table.insert(wikilinks, {start, NextClose+1}) --Note the pair
mw.log("Suspected text:", string.sub(text, start, NextClose+1))
end
end
end
if true then return end --not done!
--Step 2: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local templates = {}
local templateData = {}
while text ~= "" do
local NextTemplate = string.find(text,"{{") --Skip to next notable block
if not NextTemplate then --Done
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
end
--Step 3: Re-trace the templates using their known bounds, collecting our parameters with ease
return templates, templateData
end
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, likely not required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
p.ParseTemplates([=[
]]Hey!
A[[a]][[b]]B
Done[[
]=])
]==]
bun2gcmdzw14uwldqk36n7cp5bvbay1
281564
281563
2023-03-18T10:31:26Z
en>Aidan9382
0
+<math> and some notes
281564
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes (This needs re-doing pending new changes...........)
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
The trouble here is when the amount of opening and closing braces don't match
E.g.
{{{!}} -> { {{!}} -> {|
{{{{{A}}}} -> {{ {{{A}}} }
The rough idea is to match based off of the smallest amount of braces from the
two sides and treat the excess as regular text.
Templates beginning with the characters listed
[[Wikipedia:Page name#Technical restrictions and limitations|here]] won't be
considered as valid syntax inside {{ }}
Since we can't account for literally every scenario without it getting insane
very fast, syntax like {{{{{A}}}}} will be considered invalid, claiming {{{A}}}
is an invalid name regardless of what it resolves to. This means we don't have
to consider some complications. While this obviously isn't perfect, it's the
best approach I can form right now.
Setting dontEscape will prevent running the input text through EET. Only do this
if it's already been passed through manually.
--]=]
local function ParseTemplates(text, dontEscape) --Need to think about how the execution logic on this is gonna work, so its not done yet
if not dontEscape then
text = PrepareText(text)
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while text ~= "" do
local NextOpen = string.find(text,"%[%[", scannerPosition) or 9e9
local NextClose = string.find(text,"]]", scannerPosition) or 9e9
--mw.log("Debug time | Step 1")
--mw.log("NO, NC =", NextOpen, ",", NextClose)
--mw.logObject(wikilinks)
--mw.logObject(openWikilinks)
if NextOpen == NextClose then --Done (both 9e9)
break
end
local NextCheck = math.min(NextOpen or 9e9, NextClose or 9e9) --Skip to next notable block
scannerPosition = NextCheck+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
table.insert(wikilinks, {start, NextClose+1}) --Note the pair
--mw.log("Suspected text:", string.sub(text, start, NextClose+1))
end
end
end
if true then return end --not done!
--Step 2: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local templates = {}
local templateData = {}
while text ~= "" do
local NextTemplate = string.find(text,"{{") --Skip to next notable block
if not NextTemplate then --Done
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
end
--Step 3: Re-trace the templates using their known bounds, collecting our parameters with ease
return templates, templateData
end
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
p.ParseTemplates([=[
]]Hey!
A[[a]][[b]]B
Done[[
]=])
]==]
kn7ctq9lnpwp5trb63f822xdfbnmggg
281565
281564
2023-03-20T11:09:16Z
en>Aidan9382
0
templates now do the pairing thing
281565
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateBoundsBlacklist(...)
local blacklist = {}
local boundlist = {...}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
return blacklist
end
local function CreateBoundsBlacklistWithinBounds(container, ...) --This is getting stupid
local blacklist = {}
local boundlist = {...}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
if bound.Start > container.Start and bound.End < container.End then
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
end
return blacklist
end
local function FindWithBlacklist(text, pattern, init, blacklist, ...)
while true do
local s, e = string.find(text, pattern, init, ...)
if s then
if blacklist[s] then --illegal match
init = e+1
else --legal match
return s, e
end
else --no match
return 9e9, 9e9
end
end
end
--Main function
local function ParseTemplates(text, dontEscape)
--Setup
if not dontEscape then
text = PrepareText(text)
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(text, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(text, "]]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
table.insert(wikilinks, {Start=start, End=NextClose+1}) --Note the pair
end
end
end
local WikilinkBlacklist = CreateBoundsBlacklist(wikilinks)
--Step 2: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local scannerPosition = 1
local templates = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = FindWithBlacklist(text, "{{+", scannerPosition, WikilinkBlacklist)
local NextClose, CEnd = FindWithBlacklist(text, "}}+", scannerPosition, WikilinkBlacklist)
-- mw.log("Debug time | Step 2")
-- mw.log("NO, NC =", NextOpen, ",", NextClose)
-- mw.log("OEnd, CEnd =", OEnd, ",", CEnd)
-- mw.logObject(templates)
-- mw.logObject(openBrackets)
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(text, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(text, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --Dump the {{{Var}}} from the list
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a table (both sides have 2 spare, but at least one side doesn't have 3 spare)
-- mw.log("Suspected text:", string.sub(text, OpenSet.End-1, scannerPosition+1))
table.insert(templates, {Start=OpenSet.End-1, End=scannerPosition+1})
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the {{ / }} set
end
if true then return end --not done!
--Step 3: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local templateText = {}
local templateData = {}
for _,template in next,templates do
--Nothing right now
end
--Finished, return
return templateText, templateData
end
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
p.ParseTemplates([=[
]]Hey!
A[[a]][[b]]B
Done[[
]=])
]==]
fs9u4foutrlsdt7u9w1slechw1g63tv
281566
281565
2023-03-20T12:31:43Z
en>Aidan9382
0
they also do the argument thing
281566
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates found in string form, listed in chronological order
A table with a key-value link between a template's string form and their data
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateBoundsBlacklist(boundlist)
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
return blacklist
end
local function CreateBoundsBlacklistWithinBounds(container, boundlist) --These names are getting stupid
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
if bound.Start > container.Start and bound.End < container.End then
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
end
return blacklist
end
local function FindWithBlacklist(text, pattern, init, blacklist, ...)
while true do
local s, e = string.find(text, pattern, init, ...)
if s then
if blacklist[s] then --illegal match
init = e+1
else --legal match
return s, e
end
else --no match
return
end
end
end
local function ClearTableWhitespace(t)
local maxindex = 0
for i,_ in next,t do
maxindex = math.max(i, maxindex)
end
local nexti = 1
for i = 1,maxindex do
local d = t[i]
if d then
t[i] = nil
t[nexti] = d
nexti = nexti + 1
end
end
return t
end
--Main function
local function ParseTemplates(_text, dontEscape)
--Setup
if not dontEscape then
_text = PrepareText(_text)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(_text, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(_text, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
table.insert(wikilinks, {Start=start, End=NextClose+1}) --Note the pair
end
end
end
local WikilinkBlacklist = CreateBoundsBlacklist({wikilinks})
--Step 2: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local scannerPosition = 1
local templates = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = FindWithBlacklist(_text, "{{+", scannerPosition, WikilinkBlacklist)
local NextClose, CEnd = FindWithBlacklist(_text, "}}+", scannerPosition, WikilinkBlacklist)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(_text, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(_text, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --Dump the {{{Var}}} from the list
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a table (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
ClearTableWhitespace(templates) --Fix into chronological ordering
--Step 3: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local function HandleArgLogic(data, text, blacklist)
if data.Name then
local equals = FindWithBlacklist(text, "=", 1, blacklist)
if equals then
data.Args[finalise(mw.text.trim(string.sub(text, 1, equals-1)))] = finalise(mw.text.trim(string.sub(text, equals+1)))
else
data.Args[tostring(data._NextIndex)] = finalise(text) --not trimmed
data._NextIndex = data._NextIndex + 1
end
else
data.Name = mw.text.trim(text)
end
end
local AllTemplates = {}
local templateData = {}
for _,template in ipairs(templates) do
local InnerBlacklist = CreateBoundsBlacklistWithinBounds(template, {wikilinks, templates})
local innerText = string.sub(_text, template.Start, template.End)
local TemplateString = finalise(innerText)
table.insert(AllTemplates, TemplateString)
if not templateData[TemplateString] then
local data = {Args = {}, Text = TemplateString, _NextIndex = 1}
local scannerPosition = 3
while true do
local NextPipe = FindWithBlacklist(innerText, "|", scannerPosition, InnerBlacklist)
if not NextPipe then
HandleArgLogic(data, string.sub(innerText, scannerPosition, -3), InnerBlacklist)
break
end
HandleArgLogic(data, string.sub(innerText, scannerPosition, NextPipe-1), InnerBlacklist)
scannerPosition = NextPipe + 1
end
data._NextIndex = nil
templateData[TemplateString] = data
end
end
--Finished, return
return AllTemplates, templateData
end
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a,b = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a); mw.logObject(b)
]==]
ha9g6lx8j3z658us9vke18hidjmo50y
281567
281566
2023-03-20T13:05:46Z
en>Aidan9382
0
note
281567
Scribunto
text/plain
require("strict")
local p = {}
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates found in string form, listed in chronological order
A table with a key-value link between a template's string form and their data
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateBoundsBlacklist(boundlist)
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
return blacklist
end
local function CreateBoundsBlacklistWithinBounds(container, boundlist) --These names are getting stupid
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
if bound.Start > container.Start and bound.End < container.End then
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
end
return blacklist
end
local function FindWithBlacklist(text, pattern, init, blacklist, ...)
while true do
local s, e = string.find(text, pattern, init, ...)
if s then
if blacklist[s] then --illegal match
init = e+1
else --legal match
return s, e
end
else --no match
return
end
end
end
local function ClearTableWhitespace(t)
local maxindex = 0
for i,_ in next,t do
maxindex = math.max(i, maxindex)
end
local nexti = 1
for i = 1,maxindex do
local d = t[i]
if d then
t[i] = nil
t[nexti] = d
nexti = nexti + 1
end
end
return t
end
--Main function
local function ParseTemplates(_text, dontEscape)
--Setup
if not dontEscape then
_text = PrepareText(_text)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(_text, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(_text, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
table.insert(wikilinks, {Start=start, End=NextClose+1}) --Note the pair
end
end
end
local WikilinkBlacklist = CreateBoundsBlacklist({wikilinks})
--Step 2: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local scannerPosition = 1
local templates = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = FindWithBlacklist(_text, "{{+", scannerPosition, WikilinkBlacklist)
local NextClose, CEnd = FindWithBlacklist(_text, "}}+", scannerPosition, WikilinkBlacklist)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(_text, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(_text, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --Dump the {{{Var}}} from the list
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a table (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
ClearTableWhitespace(templates) --Fix into chronological ordering
--Step 3: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local function HandleArgLogic(data, text, blacklist)
if data.Name then
local equals = FindWithBlacklist(text, "=", 1, blacklist)
if equals then
data.Args[finalise(mw.text.trim(string.sub(text, 1, equals-1)))] = finalise(mw.text.trim(string.sub(text, equals+1)))
else
data.Args[tostring(data._NextIndex)] = finalise(text) --not trimmed
data._NextIndex = data._NextIndex + 1
end
else
data.Name = mw.text.trim(text)
end
end
local AllTemplates = {}
local templateData = {}
for _,template in ipairs(templates) do
local InnerBlacklist = CreateBoundsBlacklistWithinBounds(template, {wikilinks, templates})
local innerText = string.sub(_text, template.Start, template.End)
local TemplateString = finalise(innerText)
table.insert(AllTemplates, TemplateString)
if not templateData[TemplateString] then
local data = {Args = {}, Text = TemplateString, _NextIndex = 1}
local scannerPosition = 3
while true do
local NextPipe = FindWithBlacklist(innerText, "|", scannerPosition, InnerBlacklist)
if not NextPipe then
HandleArgLogic(data, string.sub(innerText, scannerPosition, -3), InnerBlacklist)
break
end
HandleArgLogic(data, string.sub(innerText, scannerPosition, NextPipe-1), InnerBlacklist)
scannerPosition = NextPipe + 1
end
data._NextIndex = nil
templateData[TemplateString] = data
end
end
--Finished, return
return AllTemplates, templateData
end
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a,b = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a); mw.logObject(b)
]==]
ahy7kjg6orvjn8g34bxn8lp1qgnjuk7
281568
281567
2023-03-20T15:56:09Z
en>Aidan9382
0
formatting
281568
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates found in string form, listed in chronological order
A table with a key-value link between a template's string form and their data
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateBoundsBlacklist(boundlist)
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
return blacklist
end
local function CreateBoundsBlacklistWithinBounds(container, boundlist) --These names are getting stupid
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
if bound.Start > container.Start and bound.End < container.End then
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
end
return blacklist
end
local function FindWithBlacklist(text, pattern, init, blacklist, ...)
while true do
local s, e = string.find(text, pattern, init, ...)
if s then
if blacklist[s] then --illegal match
init = e+1
else --legal match
return s, e
end
else --no match
return
end
end
end
local function ClearTableWhitespace(t)
local maxindex = 0
for i,_ in next,t do
maxindex = math.max(i, maxindex)
end
local nexti = 1
for i = 1,maxindex do
local d = t[i]
if d then
t[i] = nil
t[nexti] = d
nexti = nexti + 1
end
end
return t
end
--Main function
local function ParseTemplates(_text, dontEscape)
--Setup
if not dontEscape then
_text = PrepareText(_text)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(_text, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(_text, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
table.insert(wikilinks, {Start=start, End=NextClose+1}) --Note the pair
end
end
end
local WikilinkBlacklist = CreateBoundsBlacklist({wikilinks})
--Step 2: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local scannerPosition = 1
local templates = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = FindWithBlacklist(_text, "{{+", scannerPosition, WikilinkBlacklist)
local NextClose, CEnd = FindWithBlacklist(_text, "}}+", scannerPosition, WikilinkBlacklist)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(_text, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(_text, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --Dump the {{{Var}}} from the list
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a table (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
ClearTableWhitespace(templates) --Fix into chronological ordering
--Step 3: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local function HandleArgLogic(data, text, blacklist)
if data.Name then
local equals = FindWithBlacklist(text, "=", 1, blacklist)
if equals then
data.Args[finalise(mw.text.trim(string.sub(text, 1, equals-1)))] = finalise(mw.text.trim(string.sub(text, equals+1)))
else
data.Args[tostring(data._NextIndex)] = finalise(text) --not trimmed
data._NextIndex = data._NextIndex + 1
end
else
data.Name = mw.text.trim(text)
end
end
local AllTemplates = {}
local templateData = {}
for _,template in ipairs(templates) do
local InnerBlacklist = CreateBoundsBlacklistWithinBounds(template, {wikilinks, templates})
local innerText = string.sub(_text, template.Start, template.End)
local TemplateString = finalise(innerText)
table.insert(AllTemplates, TemplateString)
if not templateData[TemplateString] then
local data = {Args = {}, Text = TemplateString, _NextIndex = 1}
local scannerPosition = 3
while true do
local NextPipe = FindWithBlacklist(innerText, "|", scannerPosition, InnerBlacklist)
if not NextPipe then
HandleArgLogic(data, string.sub(innerText, scannerPosition, -3), InnerBlacklist)
break
end
HandleArgLogic(data, string.sub(innerText, scannerPosition, NextPipe-1), InnerBlacklist)
scannerPosition = NextPipe + 1
end
data._NextIndex = nil
templateData[TemplateString] = data
end
end
--Finished, return
return AllTemplates, templateData
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a,b = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a); mw.logObject(b)
]==]
ssjgc6hgv4a6b88u8fm2rmevvjcv76z
281569
281568
2023-03-20T18:41:11Z
en>Aidan9382
0
Remove wikilink logic - if there's a bad wikilink, theres an issue with the target content, not this. Theres no valid situation that this code does a good job for. Hopefully this improves execution time
281569
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text,"<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates found in string form, listed in chronological order
A table with a key-value link between a template's string form and their data
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateBoundsBlacklist(boundlist)
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
return blacklist
end
local function CreateBoundsBlacklistWithinBounds(container, boundlist) --These names are getting stupid
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
if bound.Start > container.Start and bound.End < container.End then
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
end
return blacklist
end
local function FindWithBlacklist(text, pattern, init, blacklist, ...)
while true do
local s, e = string.find(text, pattern, init, ...)
if s then
if blacklist[s] then --illegal match
init = e+1
else --legal match
return s, e
end
else --no match
return
end
end
end
local function ClearTableWhitespace(t)
local maxindex = 0
for i,_ in next,t do
maxindex = math.max(i, maxindex)
end
local nexti = 1
for i = 1,maxindex do
local d = t[i]
if d then
t[i] = nil
t[nexti] = d
nexti = nexti + 1
end
end
return t
end
--Main function
local function ParseTemplates(_text, dontEscape)
--Setup
if not dontEscape then
_text = PrepareText(_text)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local scannerPosition = 1
local templates = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(_text, "{{+", scannerPosition)
local NextClose, CEnd = string.find(_text, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(_text, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(_text, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --Dump the {{{Var}}} from the list
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a table (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
ClearTableWhitespace(templates) --Fix into chronological ordering
--Step 2: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local function HandleArgLogic(data, text, blacklist)
if data.Name then
local equals = FindWithBlacklist(text, "=", 1, blacklist)
if equals then
data.Args[finalise(mw.text.trim(string.sub(text, 1, equals-1)))] = finalise(mw.text.trim(string.sub(text, equals+1)))
else
data.Args[tostring(data._NextIndex)] = finalise(text) --not trimmed
data._NextIndex = data._NextIndex + 1
end
else
data.Name = mw.text.trim(text)
end
end
local AllTemplates = {}
local templateData = {}
for _,template in ipairs(templates) do
local InnerBlacklist = CreateBoundsBlacklistWithinBounds(template, {templates})
local innerText = string.sub(_text, template.Start, template.End)
local TemplateString = finalise(innerText)
table.insert(AllTemplates, TemplateString)
if not templateData[TemplateString] then
local data = {Args = {}, Text = TemplateString, _NextIndex = 1}
local scannerPosition = 3
while true do
local NextPipe = FindWithBlacklist(innerText, "|", scannerPosition, InnerBlacklist)
if not NextPipe then
HandleArgLogic(data, string.sub(innerText, scannerPosition, -3), InnerBlacklist)
break
end
HandleArgLogic(data, string.sub(innerText, scannerPosition, NextPipe-1), InnerBlacklist)
scannerPosition = NextPipe + 1
end
data._NextIndex = nil
templateData[TemplateString] = data
end
end
--Finished, return
return AllTemplates, templateData
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a,b = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a); mw.logObject(b)
]==]
63whwlm22yk8zwc0vqjchllqrq5ovgx
281570
281569
2023-03-23T07:35:32Z
en>Aidan9382
0
notes and stuff
281570
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates found in string form, listed in chronological order
A table with a key-value link between a template's string form and their data
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateBoundsBlacklist(boundlist)
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
return blacklist
end
local function CreateBoundsBlacklistWithinBounds(container, boundlist) --These names are getting stupid
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
if bound.Start > container.Start and bound.End < container.End then
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
end
return blacklist
end
local function FindWithBlacklist(text, pattern, init, blacklist, ...)
while true do
local s, e = string.find(text, pattern, init, ...)
if s then
if blacklist[s] then --illegal match
init = e+1
else --legal match
return s, e
end
else --no match
return
end
end
end
local function ClearTableWhitespace(t)
local maxindex = 0
for i,_ in next,t do
maxindex = math.max(i, maxindex)
end
local nexti = 1
for i = 1,maxindex do
local d = t[i]
if d then
t[i] = nil
t[nexti] = d
nexti = nexti + 1
end
end
return t
end
--Main function
local function ParseTemplates(_text, dontEscape)
--Setup
if not dontEscape then
_text = PrepareText(_text)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local scannerPosition = 1
local templates = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(_text, "{{+", scannerPosition)
local NextClose, CEnd = string.find(_text, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(_text, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(_text, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --Dump the {{{Var}}} from the list
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a table (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
ClearTableWhitespace(templates) --Fix into chronological ordering
--Step 2: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local function HandleArgLogic(data, text, blacklist)
if data.Name then
local equals = FindWithBlacklist(text, "=", 1, blacklist)
if equals then
data.Args[finalise(mw.text.trim(string.sub(text, 1, equals-1)))] = finalise(mw.text.trim(string.sub(text, equals+1)))
else
data.Args[tostring(data._NextIndex)] = finalise(text) --not trimmed
data._NextIndex = data._NextIndex + 1
end
else
data.Name = mw.text.trim(text)
end
end
local AllTemplates = {}
local templateData = {}
for _,template in ipairs(templates) do
local InnerBlacklist = CreateBoundsBlacklistWithinBounds(template, {templates})
local innerText = string.sub(_text, template.Start, template.End)
local TemplateString = finalise(innerText)
table.insert(AllTemplates, TemplateString)
if not templateData[TemplateString] then
local data = {Args = {}, Text = TemplateString, _NextIndex = 1}
local scannerPosition = 3
while true do
local NextPipe = FindWithBlacklist(innerText, "|", scannerPosition, InnerBlacklist)
if not NextPipe then
HandleArgLogic(data, string.sub(innerText, scannerPosition, -3), InnerBlacklist)
break
end
HandleArgLogic(data, string.sub(innerText, scannerPosition, NextPipe-1), InnerBlacklist)
scannerPosition = NextPipe + 1
end
data._NextIndex = nil
templateData[TemplateString] = data
end
end
--Finished, return
return AllTemplates, templateData
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a,b = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a); mw.logObject(b)
]==]
lkfj99hm0855s1cmdep9q3k2bmo3ife
281571
281570
2023-03-23T10:50:15Z
en>Aidan9382
0
more notes
281571
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates found in string form, listed in chronological order
A table with a key-value link between a template's string form and their data
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateBoundsBlacklist(boundlist)
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
return blacklist
end
local function CreateBoundsBlacklistWithinBounds(container, boundlist) --These names are getting stupid
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
if bound.Start > container.Start and bound.End < container.End then
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
end
return blacklist
end
local function FindWithBlacklist(text, pattern, init, blacklist, ...)
while true do
local s, e = string.find(text, pattern, init, ...)
if s then
if blacklist[s] then --illegal match
init = e+1
else --legal match
return s, e
end
else --no match
return
end
end
end
local function ClearTableWhitespace(t)
local maxindex = 0
for i,_ in next,t do
maxindex = math.max(i, maxindex)
end
local nexti = 1
for i = 1,maxindex do
local d = t[i]
if d then
t[i] = nil
t[nexti] = d
nexti = nexti + 1
end
end
return t
end
--Main function
local function ParseTemplates(_text, dontEscape)
--Setup
if not dontEscape then
_text = PrepareText(_text)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local scannerPosition = 1
local templates = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(_text, "{{+", scannerPosition)
local NextClose, CEnd = string.find(_text, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(_text, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(_text, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --Dump the {{{Var}}} from the list
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a table (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
ClearTableWhitespace(templates) --Fix into chronological ordering
--Step 2: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local function HandleArgLogic(data, text, blacklist)
if data.Name then
local equals = FindWithBlacklist(text, "=", 1, blacklist)
if equals then
data.Args[finalise(mw.text.trim(string.sub(text, 1, equals-1)))] = finalise(mw.text.trim(string.sub(text, equals+1)))
else
data.Args[tostring(data._NextIndex)] = finalise(text) --not trimmed
data._NextIndex = data._NextIndex + 1
end
else
data.Name = mw.text.trim(text)
end
end
local AllTemplates = {}
local templateData = {}
for _,template in ipairs(templates) do
local InnerBlacklist = CreateBoundsBlacklistWithinBounds(template, {templates})
local innerText = string.sub(_text, template.Start, template.End)
local TemplateString = finalise(innerText)
table.insert(AllTemplates, TemplateString)
if not templateData[TemplateString] then
local data = {Args = {}, Text = TemplateString, _NextIndex = 1}
local scannerPosition = 3
while true do
local NextPipe = FindWithBlacklist(innerText, "|", scannerPosition, InnerBlacklist)
if not NextPipe then
HandleArgLogic(data, string.sub(innerText, scannerPosition, -3), InnerBlacklist)
break
end
HandleArgLogic(data, string.sub(innerText, scannerPosition, NextPipe-1), InnerBlacklist)
scannerPosition = NextPipe + 1
end
data._NextIndex = nil
templateData[TemplateString] = data
end
end
--Finished, return
return AllTemplates, templateData
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a,b = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a); mw.logObject(b)
]==]
sdyjspivtjjrh7ywukkpwcn1xfkxndy
281572
281571
2023-03-23T10:53:05Z
en>Aidan9382
0
var rename
281572
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates found in string form, listed in chronological order
A table with a key-value link between a template's string form and their data
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateBoundsBlacklist(boundlist)
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
return blacklist
end
local function CreateBoundsBlacklistWithinBounds(container, boundlist) --These names are getting stupid
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
if bound.Start > container.Start and bound.End < container.End then
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
end
return blacklist
end
local function FindWithBlacklist(text, pattern, init, blacklist, ...)
while true do
local s, e = string.find(text, pattern, init, ...)
if s then
if blacklist[s] then --illegal match
init = e+1
else --legal match
return s, e
end
else --no match
return
end
end
end
local function ClearTableWhitespace(t)
local maxindex = 0
for i,_ in next,t do
maxindex = math.max(i, maxindex)
end
local nexti = 1
for i = 1,maxindex do
local d = t[i]
if d then
t[i] = nil
t[nexti] = d
nexti = nexti + 1
end
end
return t
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local scannerPosition = 1
local templates = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --Dump the {{{Var}}} from the list
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a table (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
ClearTableWhitespace(templates) --Fix into chronological ordering
--Step 2: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local function HandleArgLogic(data, text, blacklist)
if data.Name then
local equals = FindWithBlacklist(text, "=", 1, blacklist)
if equals then
data.Args[finalise(mw.text.trim(string.sub(text, 1, equals-1)))] = finalise(mw.text.trim(string.sub(text, equals+1)))
else
data.Args[tostring(data._NextIndex)] = finalise(text) --not trimmed
data._NextIndex = data._NextIndex + 1
end
else
data.Name = mw.text.trim(text)
end
end
local AllTemplates = {}
local templateData = {}
for _,template in ipairs(templates) do
local InnerBlacklist = CreateBoundsBlacklistWithinBounds(template, {templates})
local innerText = string.sub(InputText, template.Start, template.End)
local TemplateString = finalise(innerText)
table.insert(AllTemplates, TemplateString)
if not templateData[TemplateString] then
local data = {Args = {}, Text = TemplateString, _NextIndex = 1}
local scannerPosition = 3
while true do
local NextPipe = FindWithBlacklist(innerText, "|", scannerPosition, InnerBlacklist)
if not NextPipe then
HandleArgLogic(data, string.sub(innerText, scannerPosition, -3), InnerBlacklist)
break
end
HandleArgLogic(data, string.sub(innerText, scannerPosition, NextPipe-1), InnerBlacklist)
scannerPosition = NextPipe + 1
end
data._NextIndex = nil
templateData[TemplateString] = data
end
end
--Finished, return
return AllTemplates, templateData
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a,b = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a); mw.logObject(b)
]==]
tovkkrft4pgyxdxlosjb1ny5lb3agl8
281573
281572
2023-03-25T07:32:09Z
en>Aidan9382
0
bring back wikilink stuff, some minor changes to step 2, will work more on this soon (also blanking arg management which is horrible right now)
281573
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates found in string form, listed in chronological order
A table with a key-value link between a template's string form and their data
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateBoundsBlacklist(boundlist)
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
return blacklist
end
local function CreateBoundsBlacklistWithinBounds(container, boundlist) --These names are getting stupid
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
if bound.Start > container.Start and bound.End < container.End then
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
end
return blacklist
end
local function FindWithBlacklist(text, pattern, init, blacklist, ...)
while true do
local s, e = string.find(text, pattern, init, ...)
if s then
if blacklist[s] then --illegal match
init = e+1
else --legal match
return s, e
end
else --no match
return
end
end
end
local function ClearTableWhitespace(t) --TODO: This is probably slow
local maxindex = 0
for i,_ in next,t do
maxindex = math.max(i, maxindex)
end
local nexti = 1
for i = 1,maxindex do
local d = t[i]
if d then
t[i] = nil
t[nexti] = d
nexti = nexti + 1
end
end
return t
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
table.insert(wikilinks, {Start=start, End=NextClose+1}) --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template, figuring out if a set should be treated as {{ or {{{ as needed
local scannerPosition = 1
local templates = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --Dump the {{{Var}}} from the list
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a table (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
ClearTableWhitespace(templates) --Fix into chronological ordering
--Step 3: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local function HandleArgLogic(data, text, blacklist)
if data.Name then
local equals = FindWithBlacklist(text, "=", 1, blacklist)
if equals then
data.Args[finalise(mw.text.trim(string.sub(text, 1, equals-1)))] = finalise(mw.text.trim(string.sub(text, equals+1)))
else
data.Args[tostring(data._NextIndex)] = finalise(text) --not trimmed
data._NextIndex = data._NextIndex + 1
end
else
data.Name = mw.text.trim(text)
end
end
local AllTemplates = {}
local templateData = {}
for _,template in ipairs(templates) do
--[[ This is no good and needs re-doing
local InnerBlacklist = CreateBoundsBlacklistWithinBounds(template, {wikilinks, templates})
local innerText = string.sub(InputText, template.Start, template.End)
local TemplateString = finalise(innerText)
table.insert(AllTemplates, TemplateString)
if not templateData[TemplateString] then
local data = {Args = {}, Text = TemplateString, _NextIndex = 1}
local scannerPosition = 3
while true do
local NextPipe = FindWithBlacklist(innerText, "|", scannerPosition, InnerBlacklist)
if not NextPipe then
HandleArgLogic(data, string.sub(innerText, scannerPosition, -3), InnerBlacklist)
break
end
HandleArgLogic(data, string.sub(innerText, scannerPosition, NextPipe-1), InnerBlacklist)
scannerPosition = NextPipe + 1
end
data._NextIndex = nil
templateData[TemplateString] = data
end
--]]
end
--Finished, return
return AllTemplates, templateData
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a,b = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a); mw.logObject(b)
]==]
j4k849uh9k21xzmwielzvdyp2agzyyn
281574
281573
2023-03-26T09:49:09Z
en>Aidan9382
0
handle {{{A}}}
281574
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates found in string form, listed in chronological order
A table with a key-value link between a template's string form and their data
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateBoundsBlacklist(boundlist)
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
return blacklist
end
local function CreateBoundsBlacklistWithinBounds(container, boundlist) --These names are getting stupid
local blacklist = {}
for _,bounds in next,boundlist do
for _,bound in next,bounds do
if bound.Start > container.Start and bound.End < container.End then
for i = bound.Start,bound.End do
blacklist[i] = true
end
end
end
end
return blacklist
end
local function FindWithBlacklist(text, pattern, init, blacklist, ...)
while true do
local s, e = string.find(text, pattern, init, ...)
if s then
if blacklist[s] then --illegal match
init = e+1
else --legal match
return s, e
end
else --no match
return
end
end
end
local function ClearTableWhitespace(t) --TODO: This is probably slow
local maxindex = 0
for i,_ in next,t do
maxindex = math.max(i, maxindex)
end
local nexti = 1
for i = 1,maxindex do
local d = t[i]
if d then
t[i] = nil
t[nexti] = d
nexti = nexti + 1
end
end
return t
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
table.insert(wikilinks, {Start=start, End=NextClose+1}) --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
ClearTableWhitespace(templates) --Fix into chronological ordering
--Step 3: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local function HandleArgLogic(data, text, blacklist)
if data.Name then
local equals = FindWithBlacklist(text, "=", 1, blacklist)
if equals then
data.Args[finalise(mw.text.trim(string.sub(text, 1, equals-1)))] = finalise(mw.text.trim(string.sub(text, equals+1)))
else
data.Args[tostring(data._NextIndex)] = finalise(text) --not trimmed
data._NextIndex = data._NextIndex + 1
end
else
data.Name = mw.text.trim(text)
end
end
local AllTemplates = {}
local templateData = {}
for _,template in ipairs(templates) do
--[[ This is no good and needs re-doing
local InnerBlacklist = CreateBoundsBlacklistWithinBounds(template, {wikilinks, templates})
local innerText = string.sub(InputText, template.Start, template.End)
local TemplateString = finalise(innerText)
table.insert(AllTemplates, TemplateString)
if not templateData[TemplateString] then
local data = {Args = {}, Text = TemplateString, _NextIndex = 1}
local scannerPosition = 3
while true do
local NextPipe = FindWithBlacklist(innerText, "|", scannerPosition, InnerBlacklist)
if not NextPipe then
HandleArgLogic(data, string.sub(innerText, scannerPosition, -3), InnerBlacklist)
break
end
HandleArgLogic(data, string.sub(innerText, scannerPosition, NextPipe-1), InnerBlacklist)
scannerPosition = NextPipe + 1
end
data._NextIndex = nil
templateData[TemplateString] = data
end
--]]
end
--Finished, return
return AllTemplates, templateData
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a,b = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a); mw.logObject(b)
]==]
3zwkksqav8xek5is68rq8mlm34npwct
281575
281574
2023-03-26T12:25:24Z
en>Aidan9382
0
trim
281575
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates found in string form, listed in chronological order
A table with a key-value link between a template's string form and their data
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function ClearTableWhitespace(t) --TODO: This is probably slow
local maxindex = 0
for i,_ in next,t do
maxindex = math.max(i, maxindex)
end
local nexti = 1
for i = 1,maxindex do
local d = t[i]
if d then
t[i] = nil
t[nexti] = d
nexti = nexti + 1
end
end
return t
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
table.insert(wikilinks, {Start=start, End=NextClose+1}) --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
ClearTableWhitespace(templates) --Fix into chronological ordering
--Step 3: Re-trace the templates using their known bounds, collecting our parameters with (slight) ease
local function HandleArgLogic(data, text, blacklist)
--[[
if data.Name then
local equals = FindWithBlacklist(text, "=", 1, blacklist)
if equals then
data.Args[finalise(mw.text.trim(string.sub(text, 1, equals-1)))] = finalise(mw.text.trim(string.sub(text, equals+1)))
else
data.Args[tostring(data._NextIndex)] = finalise(text) --not trimmed
data._NextIndex = data._NextIndex + 1
end
else
data.Name = mw.text.trim(text)
end
--]]
end
local AllTemplates = {}
local templateData = {}
for _,template in ipairs(templates) do
--[[ This is no good and needs re-doing
local InnerBlacklist = CreateBoundsBlacklistWithinBounds(template, {wikilinks, templates})
local innerText = string.sub(InputText, template.Start, template.End)
local TemplateString = finalise(innerText)
table.insert(AllTemplates, TemplateString)
if not templateData[TemplateString] then
local data = {Args = {}, Text = TemplateString, _NextIndex = 1}
local scannerPosition = 3
while true do
local NextPipe = FindWithBlacklist(innerText, "|", scannerPosition, InnerBlacklist)
if not NextPipe then
HandleArgLogic(data, string.sub(innerText, scannerPosition, -3), InnerBlacklist)
break
end
HandleArgLogic(data, string.sub(innerText, scannerPosition, NextPipe-1), InnerBlacklist)
scannerPosition = NextPipe + 1
end
data._NextIndex = nil
templateData[TemplateString] = data
end
--]]
end
--Finished, return
return AllTemplates, templateData
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a,b = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a); mw.logObject(b)
]==]
nv17euou60bz8wgmf2j1jl1huv4y0fv
281576
281575
2023-03-26T18:32:17Z
en>Aidan9382
0
A bunch of code that may or may not work perfectly but it at least works somewhat (I think)
281576
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.Children = {}
Container.Name = nil
Container._Value = nil
Container._Key = nil
Container._BeyondStart = false
Container._LastIndex = 1
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self._Key then
self._Value = self._Value .. character
else
self._Key = mw.text.trim(self._Value or "")
self._Value = ""
end
else --"|" or "}"
if not Container.Name then
Container.Name = mw.text.trim(self._Value)
self._Value = nil
else
if self._Key then
self.Args[self._Key] = mw.text.trim(self._Value)
else
self.Args[tostring(self._LastIndex)] = self._Value or ""
self._LastIndex = self._LastIndex + 1
end
self._Key = nil
self._Value = nil
end
end
end
function Container:AppendText(text)
self.Text = self.Text .. text
if not self._Value then
self._Value = ""
end
self._BeyondStart = self._BeyondStart or (text ~= "{") --Ignore starting { in the kv manager
if self._BeyondStart then
self._Value = self._Value .. text
end
end
function Container:Clean()
if self._Value then
local closing = string.find(self._Value, "}+$")
if closing then
self._Value = string.sub(self._Value, 1, closing-1)
end
self:HandleArgInput("|", true) --Simulate ending
end
self._Value = nil
self._Key = nil
self._BeyondStart = nil
self._LastIndex = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
if scannedContent ~= "" then
for _,Object in next,activeObjects do
Object:AppendText(scannedContent)
end
end
scannerPosition = NNC+1
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if Character == "{" or Character == "[" then --
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
end
for _,Object in next,activeObjects do
Object:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
for _,Object in next,activeObjects do
Object:AppendText(Character)
end
if LatestObject and LatestObject.End == NNC then
LatestObject:Clean()
if LatestObject.Type == "Template" then
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
end
else --| or =
for i = 1,#activeObjects-1 do --Insert the text for everything EXCEPT the latest object
local Object = activeObjects[i]
Object:AppendText(Character)
end
LatestObject:HandleArgInput(Character)
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
0n45z4v7vhluzk2q8cck2z32vv4avg1
281577
281576
2023-03-26T18:36:01Z
en>Aidan9382
0
arg order
281577
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
Container.Name = nil
Container._Value = nil
Container._Key = nil
Container._BeyondStart = false
Container._LastIndex = 1
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self._Key then
self._Value = self._Value .. character
else
self._Key = mw.text.trim(self._Value or "")
self._Value = ""
end
else --"|" or "}"
if not Container.Name then
Container.Name = mw.text.trim(self._Value)
self._Value = nil
else
if self._Key then
self.Args[self._Key] = mw.text.trim(self._Value)
table.insert(self.ArgOrder, self._Key)
else
local Key = tostring(self._LastIndex)
self.Args[Key] = self._Value or ""
table.insert(self.ArgOrder, Key)
self._LastIndex = self._LastIndex + 1
end
self._Key = nil
self._Value = nil
end
end
end
function Container:AppendText(text)
self.Text = self.Text .. text
if not self._Value then
self._Value = ""
end
self._BeyondStart = self._BeyondStart or (text ~= "{") --Ignore starting { in the kv manager
if self._BeyondStart then
self._Value = self._Value .. text
end
end
function Container:Clean()
if self._Value then
local closing = string.find(self._Value, "}+$")
if closing then
self._Value = string.sub(self._Value, 1, closing-1)
end
self:HandleArgInput("|", true) --Simulate ending
end
self._Value = nil
self._Key = nil
self._BeyondStart = nil
self._LastIndex = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
if scannedContent ~= "" then
for _,Object in next,activeObjects do
Object:AppendText(scannedContent)
end
end
scannerPosition = NNC+1
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if Character == "{" or Character == "[" then --
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
end
for _,Object in next,activeObjects do
Object:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
for _,Object in next,activeObjects do
Object:AppendText(Character)
end
if LatestObject and LatestObject.End == NNC then
LatestObject:Clean()
if LatestObject.Type == "Template" then
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
end
else --| or =
for i = 1,#activeObjects-1 do --Insert the text for everything EXCEPT the latest object
local Object = activeObjects[i]
Object:AppendText(Character)
end
LatestObject:HandleArgInput(Character)
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
36gtpjy7scy7ugb3na1ukcs37ukqk7i
281578
281577
2023-03-26T18:37:46Z
en>Aidan9382
0
ensure LO exists
281578
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
Container.Name = nil
Container._Value = nil
Container._Key = nil
Container._BeyondStart = false
Container._LastIndex = 1
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self._Key then
self._Value = self._Value .. character
else
self._Key = mw.text.trim(self._Value or "")
self._Value = ""
end
else --"|" or "}"
if not Container.Name then
Container.Name = mw.text.trim(self._Value)
self._Value = nil
else
if self._Key then
self.Args[self._Key] = mw.text.trim(self._Value)
table.insert(self.ArgOrder, self._Key)
else
local Key = tostring(self._LastIndex)
self.Args[Key] = self._Value or ""
table.insert(self.ArgOrder, Key)
self._LastIndex = self._LastIndex + 1
end
self._Key = nil
self._Value = nil
end
end
end
function Container:AppendText(text)
self.Text = self.Text .. text
if not self._Value then
self._Value = ""
end
self._BeyondStart = self._BeyondStart or (text ~= "{") --Ignore starting { in the kv manager
if self._BeyondStart then
self._Value = self._Value .. text
end
end
function Container:Clean()
if self._Value then
local closing = string.find(self._Value, "}+$")
if closing then
self._Value = string.sub(self._Value, 1, closing-1)
end
self:HandleArgInput("|", true) --Simulate ending
end
self._Value = nil
self._Key = nil
self._BeyondStart = nil
self._LastIndex = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
if scannedContent ~= "" then
for _,Object in next,activeObjects do
Object:AppendText(scannedContent)
end
end
scannerPosition = NNC+1
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if Character == "{" or Character == "[" then --
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
end
for _,Object in next,activeObjects do
Object:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
for _,Object in next,activeObjects do
Object:AppendText(Character)
end
if LatestObject and LatestObject.End == NNC then
LatestObject:Clean()
if LatestObject.Type == "Template" then
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
end
else --| or =
for i = 1,#activeObjects-1 do --Insert the text for everything EXCEPT the latest object
local Object = activeObjects[i]
Object:AppendText(Character)
end
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
g3knmf0v4dhxfge9t2r537wkqfotbo2
281579
281578
2023-03-26T18:43:32Z
en>Aidan9382
0
Properly decode everything
281579
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value. Also theoretically faster as it does a singular pass
through the text instead of multiple gsub runs (though we shall see as this
slowly grows more complex as I theory this).
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
Container.Name = nil
Container._Value = nil
Container._Key = nil
Container._BeyondStart = false
Container._LastIndex = 1
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self._Key then
self._Value = self._Value .. character
else
self._Key = mw.text.trim(self._Value or "")
self._Value = ""
end
else --"|" or "}"
if not Container.Name then
Container.Name = mw.text.trim(self._Value)
self._Value = nil
else
self._Value = finalise(self._Value or "")
if self._Key then
self._Key = finalise(self._Key)
self.Args[self._Key] = mw.text.trim(self._Value)
table.insert(self.ArgOrder, self._Key)
else
local Key = tostring(self._LastIndex)
self.Args[Key] = self._Value or ""
table.insert(self.ArgOrder, Key)
self._LastIndex = self._LastIndex + 1
end
self._Key = nil
self._Value = nil
end
end
end
function Container:AppendText(text)
self.Text = self.Text .. finalise(text)
if not self._Value then
self._Value = ""
end
self._BeyondStart = self._BeyondStart or (text ~= "{") --Ignore starting { in the kv manager
if self._BeyondStart then
self._Value = self._Value .. text
end
end
function Container:Clean()
if self._Value then
local closing = string.find(self._Value, "}+$")
if closing then
self._Value = string.sub(self._Value, 1, closing-1)
end
self:HandleArgInput("|", true) --Simulate ending
end
self._Value = nil
self._Key = nil
self._BeyondStart = nil
self._LastIndex = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
if scannedContent ~= "" then
for _,Object in next,activeObjects do
Object:AppendText(scannedContent)
end
end
scannerPosition = NNC+1
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if Character == "{" or Character == "[" then --
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
end
for _,Object in next,activeObjects do
Object:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
for _,Object in next,activeObjects do
Object:AppendText(Character)
end
if LatestObject and LatestObject.End == NNC then
LatestObject:Clean()
if LatestObject.Type == "Template" then
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
end
else --| or =
for i = 1,#activeObjects-1 do --Insert the text for everything EXCEPT the latest object
local Object = activeObjects[i]
Object:AppendText(Character)
end
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
4vzzl8izkbcf8m6cadb4zsh0jynh9wg
281580
281579
2023-03-26T18:49:54Z
en>Aidan9382
0
minor optimisation attempt
281580
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
Container.Name = nil
Container._Value = nil
Container._Key = nil
Container._BeyondStart = false
Container._LastIndex = 1
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self._Key then
self._Value = self._Value .. character
else
self._Key = mw.text.trim(self._Value or "")
self._Value = ""
end
else --"|" or "}"
if not Container.Name then
Container.Name = mw.text.trim(self._Value)
self._Value = nil
else
self._Value = finalise(self._Value or "")
if self._Key then
self._Key = finalise(self._Key)
self.Args[self._Key] = mw.text.trim(self._Value)
table.insert(self.ArgOrder, self._Key)
else
local Key = tostring(self._LastIndex)
self.Args[Key] = self._Value or ""
table.insert(self.ArgOrder, Key)
self._LastIndex = self._LastIndex + 1
end
self._Key = nil
self._Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text = self.Text .. (ftext or text)
if not self._Value then
self._Value = ""
end
self._BeyondStart = self._BeyondStart or (text ~= "{") --Ignore starting { in the kv manager
if self._BeyondStart then
self._Value = self._Value .. text
end
end
function Container:Clean()
if self._Value then
local closing = string.find(self._Value, "}+$")
if closing then
self._Value = string.sub(self._Value, 1, closing-1)
end
self:HandleArgInput("|", true) --Simulate ending
end
self._Value = nil
self._Key = nil
self._BeyondStart = nil
self._LastIndex = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
if scannedContent ~= "" then
local fsC = finalise(scannedContent)
for _,Object in next,activeObjects do
Object:AppendText(scannedContent, fsC)
end
end
scannerPosition = NNC+1
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if Character == "{" or Character == "[" then --
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
end
for _,Object in next,activeObjects do
Object:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
for _,Object in next,activeObjects do
Object:AppendText(Character)
end
if LatestObject and LatestObject.End == NNC then
LatestObject:Clean()
if LatestObject.Type == "Template" then
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
end
else --| or =
for i = 1,#activeObjects-1 do --Insert the text for everything EXCEPT the latest object
local Object = activeObjects[i]
Object:AppendText(Character)
end
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
t0r5i3vj7pvx8s75tdizuh2msmt7z89
281581
281580
2023-03-26T19:00:39Z
en>Aidan9382
0
This is an incredibly simple optimisation logically and I'm amazed I'm only seeing it after I've made the entire system
281581
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
Container.Name = nil
Container._Value = nil
Container._Key = nil
Container._BeyondStart = false
Container._LastIndex = 1
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self._Key then
self._Value = self._Value .. character
else
self._Key = mw.text.trim(self._Value or "")
self._Value = ""
end
else --"|" or "}"
if not Container.Name then
Container.Name = mw.text.trim(self._Value)
self._Value = nil
else
self._Value = finalise(self._Value or "")
if self._Key then
self._Key = finalise(self._Key)
self.Args[self._Key] = mw.text.trim(self._Value)
table.insert(self.ArgOrder, self._Key)
else
local Key = tostring(self._LastIndex)
self.Args[Key] = self._Value or ""
table.insert(self.ArgOrder, Key)
self._LastIndex = self._LastIndex + 1
end
self._Key = nil
self._Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text = self.Text .. (ftext or text)
if not self._Value then
self._Value = ""
end
self._BeyondStart = self._BeyondStart or (text ~= "{") --Ignore starting { in the kv manager
if self._BeyondStart then
self._Value = self._Value .. text
end
end
function Container:Clean()
if self._Value then
local closing = string.find(self._Value, "}+$")
if closing then
self._Value = string.sub(self._Value, 1, closing-1)
end
self:HandleArgInput("|", true) --Simulate ending
end
self._Value = nil
self._Key = nil
self._BeyondStart = nil
self._LastIndex = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
if scannedContent ~= "" and LatestObject then
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then --
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
Container:AppendText(Character)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
LatestObject:Clean()
if LatestObject.Type == "Template" then
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
kdvmjg8bkokic6hn5djkr8ol8tpq4wv
281582
281581
2023-03-26T20:51:55Z
en>Aidan9382
0
somehow mw.text.trim is a significant cause of the speed issues, so here's an alternative approach to try remedy that
281582
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
TODO: This entire "bounds" method of exclusion is seeming to be significantly expensive. This needs proper thought to fix
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
Container.Name = nil
Container._Value = nil
Container._Key = nil
Container._BeyondStart = false
Container._LastIndex = 1
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self._Key then
self._Value = self._Value .. character
else
self._Key = cheaptrim(self._Value or "")
self._Value = ""
end
else --"|" or "}"
if not Container.Name then
Container.Name = cheaptrim(self._Value)
self._Value = nil
else
self._Value = finalise(self._Value or "")
if self._Key then
self._Key = finalise(self._Key)
self.Args[self._Key] = cheaptrim(self._Value)
table.insert(self.ArgOrder, self._Key)
else
local Key = tostring(self._LastIndex)
self.Args[Key] = self._Value
table.insert(self.ArgOrder, Key)
self._LastIndex = self._LastIndex + 1
end
self._Key = nil
self._Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text = self.Text .. (ftext or text)
if not self._Value then
self._Value = ""
end
self._BeyondStart = self._BeyondStart or (text ~= "{") --Ignore starting { in the kv manager
if self._BeyondStart then
self._Value = self._Value .. text
end
end
function Container:Clean()
if self._Value then
local closing = string.find(self._Value, "}+$")
if closing then
self._Value = string.sub(self._Value, 1, closing-1)
end
self:HandleArgInput("|", true) --Simulate ending
end
self._Value = nil
self._Key = nil
self._BeyondStart = nil
self._LastIndex = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
if scannedContent ~= "" and LatestObject then
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then --
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
Container:AppendText(Character)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
LatestObject:Clean()
if LatestObject.Type == "Template" then
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
59oo7ic65nw6bxu1qf6xvshc1an154w
281583
281582
2023-03-26T21:20:38Z
en>Aidan9382
0
losin my mind over saving execution time
281583
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function Container_HandleArgInput(self, character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self.Key then
self.Value = self.Value .. character
else
self.Key = cheaptrim(self.Value or "")
self.Value = ""
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value)
self.Value = nil
else
self.Value = self.finalise(self.Value or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
table.insert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
table.insert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
local function Container_AppendText(self, text, ftext)
self.Text = self.Text .. (ftext or text)
if not self.Value then
self.Value = ""
end
self.BeyondStart = self.BeyondStart or (text ~= "{") --Ignore starting {
if self.BeyondStart then
self.Value = self.Value .. text
end
end
local function Container_Clean(self)
if self.Value then
local closing = string.find(self.Value, "}+$")
if closing then
self.Value = string.sub(self.Value, 1, closing-1)
end
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
Container.HandleArgInput = Container_HandleArgInput
Container.AppendText = Container_AppendText
Container.Clean = Container_Clean
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
if scannedContent ~= "" and LatestObject then
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then --
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
Container:AppendText(Character)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean()
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
nuv35kz8do8q6pomq9v09c7rrmv12jz
281584
281583
2023-03-27T08:29:45Z
en>Aidan9382
0
more changes
281584
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function Container_HandleArgInput(self, character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self.Key then
self.Value = self.Value .. character
else
self.Key = cheaptrim(self.Value or "")
self.Value = ""
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value)
self.Value = nil
else
self.Value = self.finalise(self.Value or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
table.insert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
table.insert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
local function Container_AppendText(self, text, ftext)
self.Text = self.Text .. (ftext or text)
if not self.Value then
self.Value = ""
end
self.BeyondStart = self.BeyondStart or (#self.Text > 2)
if self.BeyondStart then
self.Value = self.Value .. text
end
end
local function Container_Clean(self)
if self.Value then
local closing = string.find(self.Value, "}+$")
if closing then
self.Value = string.sub(self.Value, 1, closing-1)
end
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
Container.HandleArgInput = Container_HandleArgInput
Container.AppendText = Container_AppendText
Container.Clean = Container_Clean
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then --
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean()
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
fajg8bzs06q9zov1hbi0m6ci9yoz21a
281585
281584
2023-03-27T08:37:15Z
en>Aidan9382
0
simpler
281585
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function Container_HandleArgInput(self, character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self.Key then
self.Value = self.Value .. character
else
self.Key = cheaptrim(self.Value or "")
self.Value = ""
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value)
self.Value = nil
else
self.Value = self.finalise(self.Value or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
table.insert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
table.insert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
local function Container_AppendText(self, text, ftext)
self.Text = self.Text .. (ftext or text)
if not self.Value then
self.Value = ""
end
self.BeyondStart = self.BeyondStart or (#self.Text > 2)
if self.BeyondStart then
self.Value = self.Value .. text
end
end
local function Container_Clean(self)
if self.Value then
self.Value = string.sub(self.Value, 1, -3) --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
Container.HandleArgInput = Container_HandleArgInput
Container.AppendText = Container_AppendText
Container.Clean = Container_Clean
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then --
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean()
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
15jmrdzfjtow8plrohvkujr1jiwii9r
281586
281585
2023-03-27T11:57:49Z
en>Aidan9382
0
empty comment
281586
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function Container_HandleArgInput(self, character, internalcall)
if not internalcall then
self.Text = self.Text .. character
end
if character == "=" then
if self.Key then
self.Value = self.Value .. character
else
self.Key = cheaptrim(self.Value or "")
self.Value = ""
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value)
self.Value = nil
else
self.Value = self.finalise(self.Value or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
table.insert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
table.insert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
local function Container_AppendText(self, text, ftext)
self.Text = self.Text .. (ftext or text)
if not self.Value then
self.Value = ""
end
self.BeyondStart = self.BeyondStart or (#self.Text > 2)
if self.BeyondStart then
self.Value = self.Value .. text
end
end
local function Container_Clean(self)
if self.Value then
self.Value = string.sub(self.Value, 1, -3) --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = ""
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
Container.HandleArgInput = Container_HandleArgInput
Container.AppendText = Container_AppendText
Container.Clean = Container_Clean
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean()
table.insert(finalObjects, LatestObject)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
8oy6qv0ql5awprvxzcmvjlj2oox8zdt
281587
281586
2023-03-29T11:34:35Z
en>Aidan9382
0
Better than constant string concat
281587
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function Container_HandleArgInput(self, character, internalcall)
if not internalcall then
table.insert(self.Text, character)
end
if character == "=" then
if self.Key then
self.Value = self.Value .. character
else
self.Key = cheaptrim(self.Value or "")
self.Value = ""
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value)
self.Value = nil
else
self.Value = self.finalise(self.Value or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
table.insert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
table.insert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
local function Container_AppendText(self, text, ftext)
table.insert(self.Text, (ftext or text))
if not self.Value then
self.Value = ""
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text,"") > 2)
if self.BeyondStart then
self.Value = self.Value .. text
end
end
local function Container_Clean(self, IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = string.sub(self.Value, 1, -3) --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
Container.HandleArgInput = Container_HandleArgInput
Container.AppendText = Container_AppendText
Container.Clean = Container_Clean
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
table.insert(finalObjects, LatestObject)
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
dsqn780egd3bwai7ssjuyazs2y2kgbd
281588
281587
2023-03-29T11:48:26Z
en>Aidan9382
0
better for memory usage, dunno about speed
281588
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function Container_HandleArgInput(self, character, internalcall)
if not internalcall then
table.insert(self.Text, character)
end
if character == "=" then
if self.Key then
table.insert(self.Value, character)
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
table.insert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
table.insert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
local function Container_AppendText(self, text, ftext)
table.insert(self.Text, (ftext or text))
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
table.insert(self.Value, text)
end
end
local function Container_Clean(self, IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
Container.HandleArgInput = Container_HandleArgInput
Container.AppendText = Container_AppendText
Container.Clean = Container_Clean
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
table.insert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
table.insert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
table.insert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
table.insert(LatestObject.Children, Container)
end
table.insert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
table.insert(finalObjects, LatestObject)
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
table.insert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
table.insert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
juyzlzrilrc0bbn2cm35dj78wmjdng0
281589
281588
2023-03-29T11:54:36Z
en>Aidan9382
0
speed
281589
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
local function cheapinsert(a, b) --Faster than table.insert(a, b, no c)
a[#a+1] = b
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function Container_HandleArgInput(self, character, internalcall)
if not internalcall then
cheapinsert(self.Text, character)
end
if character == "=" then
if self.Key then
cheapinsert(self.Value, character)
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
cheapinsert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
cheapinsert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
local function Container_AppendText(self, text, ftext)
cheapinsert(self.Text, (ftext or text))
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
cheapinsert(self.Value, text)
end
end
local function Container_Clean(self, IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
Container.HandleArgInput = Container_HandleArgInput
Container.AppendText = Container_AppendText
Container.Clean = Container_Clean
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local NextOpen = string.find(InputText, "%[%[", scannerPosition) or 9e9
local NextClose = string.find(InputText, "%]%]", scannerPosition) or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
scannerPosition = math.min(NextOpen, NextClose)+2 --+2 to pass the [[ / ]]
if NextOpen < NextClose then --Add a [[ to the pending wikilink queue
cheapinsert(openWikilinks, NextOpen)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=NextClose+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local NextOpen, OEnd = string.find(InputText, "{{+", scannerPosition)
local NextClose, CEnd = string.find(InputText, "}}+", scannerPosition)
NextOpen = NextOpen or 9e9
NextClose = NextClose or 9e9
if NextOpen == NextClose then --Done (both 9e9)
break
end
local BoundStart = math.min(NextOpen, NextClose) --Skip to next notable block
local BoundEnd = math.min(OEnd or 9e9, CEnd or 9e9)
scannerPosition = BoundStart --Get to the {{ / }} set
if NextOpen < NextClose then --Add the {{+ set to the queue
local BracketCount = #string.match(InputText, "^{+", scannerPosition)
cheapinsert(openBrackets, {Start=BoundStart, End=BoundEnd})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = #string.match(InputText, "^}+", scannerPosition)
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
cheapinsert(openBrackets, OpenSet)
end
end
end
scannerPosition = BoundEnd --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
cheapinsert(LatestObject.Children, Container)
end
cheapinsert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
cheapinsert(finalObjects, LatestObject)
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
cheapinsert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
cheapinsert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
80qz45wp7c2rhdq5v8ra9hqbc7mvtd8
281590
281589
2023-04-06T13:23:27Z
en>Aidan9382
0
This is a better approach
281590
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
local function cheapinsert(a, b) --Faster than table.insert(a, b, no c)
a[#a+1] = b
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
local function Container_HandleArgInput(self, character, internalcall)
if not internalcall then
cheapinsert(self.Text, character)
end
if character == "=" then
if self.Key then
cheapinsert(self.Value, character)
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
cheapinsert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
cheapinsert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
local function Container_AppendText(self, text, ftext)
cheapinsert(self.Text, (ftext or text))
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
cheapinsert(self.Value, text)
end
end
local function Container_Clean(self, IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
Container.HandleArgInput = Container_HandleArgInput
Container.AppendText = Container_AppendText
Container.Clean = Container_Clean
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done (both 9e9)
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
cheapinsert(openWikilinks, Position)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
cheapinsert(openBrackets, {Start=Start, End=End})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
cheapinsert(openBrackets, OpenSet)
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition) --NNC = NextNotableCharacter
if not NNC then
break
end
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
cheapinsert(LatestObject.Children, Container)
end
cheapinsert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
cheapinsert(finalObjects, LatestObject)
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
cheapinsert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
cheapinsert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
bspotq94z6qpw3w45m5ftww6oe7gt1n
281591
281590
2023-04-17T19:05:34Z
en>Aidan9382
0
partial cleanup, some code move-around, some minor optimisations (probably)
281591
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
local function cheapinsert(a, b) --Faster than table.insert(a, b, no c)
a[#a+1] = b
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
cheapinsert(self.Text, character)
end
if character == "=" then
if self.Key then
cheapinsert(self.Value, character)
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
cheapinsert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
cheapinsert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
cheapinsert(self.Text, (ftext or text))
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
cheapinsert(self.Value, text)
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
cheapinsert(openWikilinks, Position)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
cheapinsert(openBrackets, {Start=Start, End=End})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
cheapinsert(openBrackets, OpenSet)
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
cheapinsert(LatestObject.Children, Container)
end
cheapinsert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
cheapinsert(finalObjects, LatestObject)
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
cheapinsert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
cheapinsert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
2fdyrbqzcxvopyab84j3yb648la35q6
281592
281591
2023-04-23T17:29:26Z
en>Aidan9382
0
Aidan9382 moved page [[Module:Sandbox/Aidan9382/ExcessiveParsing]] to [[Module:Wikitext Parsing]] without leaving a redirect: Intend to use in a live module, so moving out of "user"space
281591
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
local function cheapinsert(a, b) --Faster than table.insert(a, b, no c)
a[#a+1] = b
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
cheapinsert(self.Text, character)
end
if character == "=" then
if self.Key then
cheapinsert(self.Value, character)
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
cheapinsert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
cheapinsert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
cheapinsert(self.Text, (ftext or text))
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
cheapinsert(self.Value, text)
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
cheapinsert(openWikilinks, Position)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
cheapinsert(openBrackets, {Start=Start, End=End})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
cheapinsert(openBrackets, OpenSet)
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
cheapinsert(LatestObject.Children, Container)
end
cheapinsert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
cheapinsert(finalObjects, LatestObject)
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
cheapinsert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
cheapinsert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
2fdyrbqzcxvopyab84j3yb648la35q6
281593
281592
2023-04-23T17:40:41Z
en>Aidan9382
0
test with tables
281593
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
local function cheapinsert(a, b) --Faster than table.insert(a, b, no c)
a[#a+1] = b
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = newtext
break
end
newtext[#newtext+1] = string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
cheapinsert(self.Text, character)
end
if character == "=" then
if self.Key then
cheapinsert(self.Value, character)
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
cheapinsert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
cheapinsert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
cheapinsert(self.Text, (ftext or text))
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
cheapinsert(self.Value, text)
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
cheapinsert(openWikilinks, Position)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
cheapinsert(openBrackets, {Start=Start, End=End})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
cheapinsert(openBrackets, OpenSet)
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
cheapinsert(LatestObject.Children, Container)
end
cheapinsert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
cheapinsert(finalObjects, LatestObject)
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
cheapinsert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
cheapinsert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
0ypzt5up0stkcggyvnmjjoszburl67w
281594
281593
2023-04-23T17:41:49Z
en>Aidan9382
0
I just need to quickly confirm if this change is really saving as much as it claims it did
281594
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
local function cheapinsert(a, b) --Faster than table.insert(a, b, no c)
a[#a+1] = b
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = ""
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext = newtext .. text
break
end
newtext = newtext .. string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext = newtext .. Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext = newtext .. newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext = newtext .. string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return newtext
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
cheapinsert(self.Text, character)
end
if character == "=" then
if self.Key then
cheapinsert(self.Value, character)
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
cheapinsert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
cheapinsert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
cheapinsert(self.Text, (ftext or text))
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
cheapinsert(self.Value, text)
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
cheapinsert(openWikilinks, Position)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
cheapinsert(openBrackets, {Start=Start, End=End})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
cheapinsert(openBrackets, OpenSet)
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
cheapinsert(LatestObject.Children, Container)
end
cheapinsert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
cheapinsert(finalObjects, LatestObject)
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
cheapinsert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
cheapinsert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
2fdyrbqzcxvopyab84j3yb648la35q6
281595
281594
2023-04-23T17:42:32Z
en>Aidan9382
0
Re-test the supposedly (2+)x better option
281595
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
local function cheapinsert(a, b) --Faster than table.insert(a, b, no c)
a[#a+1] = b
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = newtext
break
end
newtext[#newtext+1] = string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
cheapinsert(self.Text, character)
end
if character == "=" then
if self.Key then
cheapinsert(self.Value, character)
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
cheapinsert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
cheapinsert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
cheapinsert(self.Text, (ftext or text))
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
cheapinsert(self.Value, text)
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
cheapinsert(openWikilinks, Position)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
cheapinsert(openBrackets, {Start=Start, End=End})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
cheapinsert(openBrackets, OpenSet)
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
cheapinsert(LatestObject.Children, Container)
end
cheapinsert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
cheapinsert(finalObjects, LatestObject)
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
cheapinsert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
cheapinsert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
0ypzt5up0stkcggyvnmjjoszburl67w
281596
281595
2023-04-23T17:44:23Z
en>Aidan9382
0
woops, im just dumb
281596
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
local function cheapinsert(a, b) --Faster than table.insert(a, b, no c)
a[#a+1] = b
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = text
break
end
newtext[#newtext+1] = string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
cheapinsert(self.Text, character)
end
if character == "=" then
if self.Key then
cheapinsert(self.Value, character)
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
cheapinsert(self.ArgOrder, self.Key)
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
cheapinsert(self.ArgOrder, Key)
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
cheapinsert(self.Text, (ftext or text))
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
cheapinsert(self.Value, text)
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
cheapinsert(openWikilinks, Position)
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
cheapinsert(openBrackets, {Start=Start, End=End})
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
cheapinsert(openBrackets, OpenSet)
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
cheapinsert(LatestObject.Children, Container)
end
cheapinsert(activeObjects, Container)
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
cheapinsert(finalObjects, LatestObject)
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
cheapinsert(SortableReference, Object.Start)
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
cheapinsert(FixedOrder, Object)
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
deowede1f6kr6tbjmunj0onvvb3co5b
281597
281596
2023-04-23T17:47:00Z
en>Aidan9382
0
remove cheapinsert calls and just inline
281597
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text)
local tagName = (string.match(text, "^<([^\n />]+)") or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", 2) or -1
local nextCloser = string.find(text, ">", 2) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, 1, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>") or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>")
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>")
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text) --Like TestForNowikiTag but for <!-- -->
if startswith(text, "<!--") then
local commentEnd = string.find(text, "-->", 5, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, 5, commentEnd-1),
Length = commentEnd+2
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, 5),
Length = #text
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
while text ~= "" do
local NextCheck = string.find(text, "<[NnSsPpMm!]") --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = text
break
end
newtext[#newtext+1] = string.sub(text,1,NextCheck-1)
text = string.sub(text, NextCheck)
local Comment = TestForComment(text)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
text = string.sub(text, Comment.Length+1)
else
local Tag = TestForNowikiTag(text)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
text = string.sub(text, Tag.Length+1)
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, 1, 1)
text = string.sub(text, 2)
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text[#self.Text+1] = character
end
if character == "=" then
if self.Key then
self.Value[#self.Value+1] = character
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
self.ArgOrder[#self.ArgOrder+1] = self.Key
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
self.ArgOrder[#self.ArgOrder+1] = Key
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text[#self.Text+1] = (ftext or text)
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
self.Value[#self.Value+1] = text
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
openWikilinks[#openWikilinks+1] = Position
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
openBrackets[#openBrackets+1] = {Start=Start, End=End}
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
openBrackets[#openBrackets+1] = OpenSet
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
LatestObject.Children[#LatestObject.Children+1] = Container
end
activeObjects[#activeObjects+1] = Container
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
finalObjects[#finalObjects+1] = LatestObject
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
SortableReference[#SortableReference+1] = Object.Start
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
FixedOrder[#FixedOrder+1] = Object
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[<!--
Hey!
-->A]=]
local out = p.TestForComments(s)
mw.logObject(out); mw.log(string.sub(s, 1, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
2ikrcfr4y22a4x87fgq8fqjod9fkp1b
281598
281597
2023-04-23T18:10:01Z
en>Aidan9382
0
experiment with scanPosition
281598
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text, scanPosition)
local tagName = (string.match(text, "^<([^\n />]+)", scanPosition) or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", scanPosition+1) or -1
local nextCloser = string.find(text, ">", scanPosition+1) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, scanPosition, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>", scanPosition) or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>", scanPosition)
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>", scanPosition)
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text, scanPosition) --Like TestForNowikiTag but for <!-- -->
if string.match(text, "^<!--", scanPosition) then
local commentEnd = string.find(text, "-->", scanPosition+4, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, scanPosition+4, commentEnd-1),
Length = commentEnd-scanPosition+3
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, scanPosition+4),
Length = #text-scanPosition+1
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
local scanPosition = 1
while true do
local NextCheck = string.find(text, "<[NnSsPpMm!]", scanPosition) --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = string.sub(text,scanPosition)
break
end
newtext[#newtext+1] = string.sub(text,scanPosition,NextCheck-1)
scanPosition = NextCheck
local Comment = TestForComment(text, scanPosition)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
scanPosition = scanPosition + Comment.Length
else
local Tag = TestForNowikiTag(text, scanPosition)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
scanPosition = scanPosition + Tag.Length
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, scanPosition, scanPosition)
scanPosition = scanPosition + 1
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text[#self.Text+1] = character
end
if character == "=" then
if self.Key then
self.Value[#self.Value+1] = character
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
self.ArgOrder[#self.ArgOrder+1] = self.Key
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
self.ArgOrder[#self.ArgOrder+1] = Key
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text[#self.Text+1] = (ftext or text)
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
self.Value[#self.Value+1] = text
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
openWikilinks[#openWikilinks+1] = Position
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
openBrackets[#openBrackets+1] = {Start=Start, End=End}
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
openBrackets[#openBrackets+1] = OpenSet
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
LatestObject.Children[#LatestObject.Children+1] = Container
end
activeObjects[#activeObjects+1] = Container
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
finalObjects[#finalObjects+1] = LatestObject
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
SortableReference[#SortableReference+1] = Object.Start
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
FixedOrder[#FixedOrder+1] = Object
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[B<!--
Hey!
-->A]=]
local out = p.TestForComment(s, 2)
mw.logObject(out); mw.log(string.sub(s, 2, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
2svve3howx7kscwv79zqrxhyfo0ll0d
281599
281598
2023-04-23T19:05:06Z
en>Aidan9382
0
regex correction for the start of TFC
281599
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text, scanPosition)
local tagName = (string.match(text, "^<([^\n />]+)", scanPosition) or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", scanPosition+1) or -1
local nextCloser = string.find(text, ">", scanPosition+1) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, scanPosition, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>", scanPosition) or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>", scanPosition)
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>", scanPosition)
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text, scanPosition) --Like TestForNowikiTag but for <!-- -->
if string.match(text, "^<!%-%-", scanPosition) then
local commentEnd = string.find(text, "-->", scanPosition+4, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, scanPosition+4, commentEnd-1),
Length = commentEnd-scanPosition+3
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, scanPosition+4),
Length = #text-scanPosition+1
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
local scanPosition = 1
while true do
local NextCheck = string.find(text, "<[NnSsPpMm!]", scanPosition) --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = string.sub(text,scanPosition)
break
end
newtext[#newtext+1] = string.sub(text,scanPosition,NextCheck-1)
scanPosition = NextCheck
local Comment = TestForComment(text, scanPosition)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
scanPosition = scanPosition + Comment.Length
else
local Tag = TestForNowikiTag(text, scanPosition)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
scanPosition = scanPosition + Tag.Length
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, scanPosition, scanPosition)
scanPosition = scanPosition + 1
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text[#self.Text+1] = character
end
if character == "=" then
if self.Key then
self.Value[#self.Value+1] = character
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
self.ArgOrder[#self.ArgOrder+1] = self.Key
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
self.ArgOrder[#self.ArgOrder+1] = Key
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text[#self.Text+1] = (ftext or text)
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
self.Value[#self.Value+1] = text
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
openWikilinks[#openWikilinks+1] = Position
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
openBrackets[#openBrackets+1] = {Start=Start, End=End}
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
openBrackets[#openBrackets+1] = OpenSet
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
LatestObject.Children[#LatestObject.Children+1] = Container
end
activeObjects[#activeObjects+1] = Container
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
finalObjects[#finalObjects+1] = LatestObject
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
SortableReference[#SortableReference+1] = Object.Start
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
FixedOrder[#FixedOrder+1] = Object
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[B<!--
Hey!
-->A]=]
local out = p.TestForComment(s, 2)
mw.logObject(out); mw.log(string.sub(s, 2, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
70zc28exu6snc7uxb8qp17ngkkauhm0
281600
281599
2023-04-24T18:00:07Z
en>MusikBot II
0
Protected "[[Module:Wikitext Parsing]]": [[Wikipedia:High-risk templates|High-risk template or module]]: 680413 transclusions ([[User:MusikBot II/TemplateProtector|more info]]) ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
281599
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces are allowed in closing tags (except in <pre> tags, which
follow the rules of a regular html tag for formatting).
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text, scanPosition)
local tagName = (string.match(text, "^<([^\n />]+)", scanPosition) or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", scanPosition+1) or -1
local nextCloser = string.find(text, ">", scanPosition+1) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, scanPosition, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTag
if tagName == "pre" then --Looser restrictions for <pre>
endingTag = --no | so we just use 2 matches
string.match(text, "</[Pp][Rr][Ee]>", scanPosition) or
string.match(text, "</[Pp][Rr][Ee][ \t\n/][^<]*>", scanPosition)
else
endingTag = string.match(text, "</"..allcases(tagName).."[ \t\n]*>", scanPosition)
end
if endingTag then --Regular tag formation
local endingTagPosition = string.find(text, endingTag, nextCloser, true)
local tagContent = string.sub(text, nextCloser+1, endingTagPosition-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text, scanPosition) --Like TestForNowikiTag but for <!-- -->
if string.match(text, "^<!%-%-", scanPosition) then
local commentEnd = string.find(text, "-->", scanPosition+4, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, scanPosition+4, commentEnd-1),
Length = commentEnd-scanPosition+3
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, scanPosition+4),
Length = #text-scanPosition+1
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
local scanPosition = 1
while true do
local NextCheck = string.find(text, "<[NnSsPpMm!]", scanPosition) --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = string.sub(text,scanPosition)
break
end
newtext[#newtext+1] = string.sub(text,scanPosition,NextCheck-1)
scanPosition = NextCheck
local Comment = TestForComment(text, scanPosition)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
scanPosition = scanPosition + Comment.Length
else
local Tag = TestForNowikiTag(text, scanPosition)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
scanPosition = scanPosition + Tag.Length
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, scanPosition, scanPosition)
scanPosition = scanPosition + 1
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text[#self.Text+1] = character
end
if character == "=" then
if self.Key then
self.Value[#self.Value+1] = character
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
self.ArgOrder[#self.ArgOrder+1] = self.Key
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
self.ArgOrder[#self.ArgOrder+1] = Key
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text[#self.Text+1] = (ftext or text)
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
self.Value[#self.Value+1] = text
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
openWikilinks[#openWikilinks+1] = Position
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
openBrackets[#openBrackets+1] = {Start=Start, End=End}
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
openBrackets[#openBrackets+1] = OpenSet
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
LatestObject.Children[#LatestObject.Children+1] = Container
end
activeObjects[#activeObjects+1] = Container
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
finalObjects[#finalObjects+1] = LatestObject
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
SortableReference[#SortableReference+1] = Object.Start
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
FixedOrder[#FixedOrder+1] = Object
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[B<!--
Hey!
-->A]=]
local out = p.TestForComment(s, 2)
mw.logObject(out); mw.log(string.sub(s, 2, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
70zc28exu6snc7uxb8qp17ngkkauhm0
281601
281600
2024-01-31T21:54:57Z
en>Aidan9382
0
Fix weird behaviour with <pre> tags; simplify tag-end searching to avoid double-scanning
281601
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source, etc.) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces/tabs are allowed in closing tags.
Note that, even though <pre> tags cause a visual change when the ending tag has
extra formatting, it won't cause the no-processing effects. For some reason, the
format must be strict for that to apply.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text, scanPosition)
local tagName = (string.match(text, "^<([^\n />]+)", scanPosition) or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", scanPosition+1) or -1
local nextCloser = string.find(text, ">", scanPosition+1) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, scanPosition, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTagStart, endingTagEnd = string.find(text, "</"..allcases(tagName).."[ \t\n]*>", scanPosition)
if endingTagStart then --Regular tag formation
local endingTag = string.sub(text, endingTagStart, endingTagEnd)
local tagContent = string.sub(text, nextCloser+1, endingTagStart-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text, scanPosition) --Like TestForNowikiTag but for <!-- -->
if string.match(text, "^<!%-%-", scanPosition) then
local commentEnd = string.find(text, "-->", scanPosition+4, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, scanPosition+4, commentEnd-1),
Length = commentEnd-scanPosition+3
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, scanPosition+4),
Length = #text-scanPosition+1
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
local scanPosition = 1
while true do
local NextCheck = string.find(text, "<[NnSsPpMm!]", scanPosition) --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = string.sub(text,scanPosition)
break
end
newtext[#newtext+1] = string.sub(text,scanPosition,NextCheck-1)
scanPosition = NextCheck
local Comment = TestForComment(text, scanPosition)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
scanPosition = scanPosition + Comment.Length
else
local Tag = TestForNowikiTag(text, scanPosition)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
scanPosition = scanPosition + Tag.Length
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, scanPosition, scanPosition)
scanPosition = scanPosition + 1
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text[#self.Text+1] = character
end
if character == "=" then
if self.Key then
self.Value[#self.Value+1] = character
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
self.ArgOrder[#self.ArgOrder+1] = self.Key
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
self.ArgOrder[#self.ArgOrder+1] = Key
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text[#self.Text+1] = (ftext or text)
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
self.Value[#self.Value+1] = text
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
openWikilinks[#openWikilinks+1] = Position
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
openBrackets[#openBrackets+1] = {Start=Start, End=End}
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
openBrackets[#openBrackets+1] = OpenSet
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
LatestObject.Children[#LatestObject.Children+1] = Container
end
activeObjects[#activeObjects+1] = Container
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
finalObjects[#finalObjects+1] = LatestObject
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
SortableReference[#SortableReference+1] = Object.Start
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
FixedOrder[#FixedOrder+1] = Object
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[B<!--
Hey!
-->A]=]
local out = p.TestForComment(s, 2)
mw.logObject(out); mw.log(string.sub(s, 2, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
a8vxa9vy6taffs22l6y73yxjlk3v8y7
281602
281601
2024-02-22T04:42:24Z
en>Pppery
0
Changed protection settings for "[[Module:Wikitext Parsing]]": [[WP:High-risk templates|High-risk template or module]]: This is, among other things, an indirect dependency of [[Module:Pagetype]] and hence [[Template:Short description]], and hence used on 26% of all pages ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite))
281601
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
for i = 1,#str do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = #str,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source, etc.) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces/tabs are allowed in closing tags.
Note that, even though <pre> tags cause a visual change when the ending tag has
extra formatting, it won't cause the no-processing effects. For some reason, the
format must be strict for that to apply.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text, scanPosition)
local tagName = (string.match(text, "^<([^\n />]+)", scanPosition) or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", scanPosition+1) or -1
local nextCloser = string.find(text, ">", scanPosition+1) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, scanPosition, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTagStart, endingTagEnd = string.find(text, "</"..allcases(tagName).."[ \t\n]*>", scanPosition)
if endingTagStart then --Regular tag formation
local endingTag = string.sub(text, endingTagStart, endingTagEnd)
local tagContent = string.sub(text, nextCloser+1, endingTagStart-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text, scanPosition) --Like TestForNowikiTag but for <!-- -->
if string.match(text, "^<!%-%-", scanPosition) then
local commentEnd = string.find(text, "-->", scanPosition+4, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, scanPosition+4, commentEnd-1),
Length = commentEnd-scanPosition+3
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, scanPosition+4),
Length = #text-scanPosition+1
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
local scanPosition = 1
while true do
local NextCheck = string.find(text, "<[NnSsPpMm!]", scanPosition) --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = string.sub(text,scanPosition)
break
end
newtext[#newtext+1] = string.sub(text,scanPosition,NextCheck-1)
scanPosition = NextCheck
local Comment = TestForComment(text, scanPosition)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
scanPosition = scanPosition + Comment.Length
else
local Tag = TestForNowikiTag(text, scanPosition)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
scanPosition = scanPosition + Tag.Length
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, scanPosition, scanPosition)
scanPosition = scanPosition + 1
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text[#self.Text+1] = character
end
if character == "=" then
if self.Key then
self.Value[#self.Value+1] = character
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
self.ArgOrder[#self.ArgOrder+1] = self.Key
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
self.ArgOrder[#self.ArgOrder+1] = Key
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text[#self.Text+1] = (ftext or text)
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
self.Value[#self.Value+1] = text
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
openWikilinks[#openWikilinks+1] = Position
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
openBrackets[#openBrackets+1] = {Start=Start, End=End}
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
openBrackets[#openBrackets+1] = OpenSet
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
LatestObject.Children[#LatestObject.Children+1] = Container
end
activeObjects[#activeObjects+1] = Container
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
finalObjects[#finalObjects+1] = LatestObject
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
SortableReference[#SortableReference+1] = Object.Start
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
FixedOrder[#FixedOrder+1] = Object
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[B<!--
Hey!
-->A]=]
local out = p.TestForComment(s, 2)
mw.logObject(out); mw.log(string.sub(s, 2, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
a8vxa9vy6taffs22l6y73yxjlk3v8y7
281603
281602
2025-05-05T17:47:12Z
en>Dinoguy1000
0
the Lua # operator recalculates the length every time it's accessed (including on every loop in a for), so cache its value in cheaptrim since the string isn't modified in-place
281603
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
local strlen = #str
for i = 1,strlen do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = strlen,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source, etc.) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces/tabs are allowed in closing tags.
Note that, even though <pre> tags cause a visual change when the ending tag has
extra formatting, it won't cause the no-processing effects. For some reason, the
format must be strict for that to apply.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text, scanPosition)
local tagName = (string.match(text, "^<([^\n />]+)", scanPosition) or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", scanPosition+1) or -1
local nextCloser = string.find(text, ">", scanPosition+1) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, scanPosition, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTagStart, endingTagEnd = string.find(text, "</"..allcases(tagName).."[ \t\n]*>", scanPosition)
if endingTagStart then --Regular tag formation
local endingTag = string.sub(text, endingTagStart, endingTagEnd)
local tagContent = string.sub(text, nextCloser+1, endingTagStart-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text, scanPosition) --Like TestForNowikiTag but for <!-- -->
if string.match(text, "^<!%-%-", scanPosition) then
local commentEnd = string.find(text, "-->", scanPosition+4, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, scanPosition+4, commentEnd-1),
Length = commentEnd-scanPosition+3
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, scanPosition+4),
Length = #text-scanPosition+1
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
local scanPosition = 1
while true do
local NextCheck = string.find(text, "<[NnSsPpMm!]", scanPosition) --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = string.sub(text,scanPosition)
break
end
newtext[#newtext+1] = string.sub(text,scanPosition,NextCheck-1)
scanPosition = NextCheck
local Comment = TestForComment(text, scanPosition)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
scanPosition = scanPosition + Comment.Length
else
local Tag = TestForNowikiTag(text, scanPosition)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
scanPosition = scanPosition + Tag.Length
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, scanPosition, scanPosition)
scanPosition = scanPosition + 1
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text[#self.Text+1] = character
end
if character == "=" then
if self.Key then
self.Value[#self.Value+1] = character
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
self.ArgOrder[#self.ArgOrder+1] = self.Key
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
self.ArgOrder[#self.ArgOrder+1] = Key
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text[#self.Text+1] = (ftext or text)
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
self.Value[#self.Value+1] = text
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
openWikilinks[#openWikilinks+1] = Position
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
openBrackets[#openBrackets+1] = {Start=Start, End=End}
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
openBrackets[#openBrackets+1] = OpenSet
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
LatestObject.Children[#LatestObject.Children+1] = Container
end
activeObjects[#activeObjects+1] = Container
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
finalObjects[#finalObjects+1] = LatestObject
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
SortableReference[#SortableReference+1] = Object.Start
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
FixedOrder[#FixedOrder+1] = Object
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[B<!--
Hey!
-->A]=]
local out = p.TestForComment(s, 2)
mw.logObject(out); mw.log(string.sub(s, 2, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
1pc5nwfjm9r5mkdrg3s2ci608uixfb7
281604
281603
2026-06-07T19:29:47Z
Ameisenigel
44
60 revisions imported from [[:en:Module:Wikitext_Parsing]]: Request at [[WF:AN]]
281603
Scribunto
text/plain
require("strict")
--Helper functions
local function startswith(text, subtext)
return string.sub(text, 1, #subtext) == subtext
end
local function endswith(text, subtext)
return string.sub(text, -#subtext, -1) == subtext
end
local function allcases(s)
return s:gsub("%a", function(c)
return "["..c:upper()..c:lower().."]"
end)
end
local trimcache = {}
local whitespace = {[" "]=1, ["\n"]=1, ["\t"]=1, ["\r"]=1}
local function cheaptrim(str) --mw.text.trim is surprisingly expensive, so here's an alternative approach
local quick = trimcache[str]
if quick then
return quick
else
-- local out = string.gsub(str, "^%s*(.-)%s*$", "%1")
local lowEnd
local strlen = #str
for i = 1,strlen do
if not whitespace[string.sub(str, i, i)] then
lowEnd = i
break
end
end
if not lowEnd then
trimcache[str] = ""
return ""
end
for i = strlen,1,-1 do
if not whitespace[string.sub(str, i, i)] then
local out = string.sub(str, lowEnd, i)
trimcache[str] = out
return out
end
end
end
end
--[=[ Implementation notes
---- NORMAL HTML TAGS ----
Tags are very strict on how they want to start, but loose on how they end.
The start must strictly follow <[tAgNaMe](%s|>) with no room for whitespace in
the tag's name, but may then flow as they want afterwards, making
<div\nclass\n=\n"\nerror\n"\n> valid
There's no sense of escaping < or >
E.g.
<div class="error\>"> will end at \> despite it being inside a quote
<div class="<span class="error">error</span>"> will not process the larger div
If a tag has no end, it will consume all text instead of not processing
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source, etc.) ----
(In most comments, <source> will not be mentioned. This is because it is the
deprecated version of <syntaxhighlight>)
No-Processing tags have some interesting differences to the above rules.
For example, their syntax is a lot stricter. While an opening tag appears to
follow the same set of rules, A closing tag can't have any sort of extra
formatting period. While </div a/a> is valid, </nowiki a/a> isn't - only
newlines and spaces/tabs are allowed in closing tags.
Note that, even though <pre> tags cause a visual change when the ending tag has
extra formatting, it won't cause the no-processing effects. For some reason, the
format must be strict for that to apply.
Both the content inside the tag pair and the content inside each side of the
pair is not processed. E.g. <nowiki |}}>|}}</nowiki> would have both of the |}}
escaped in practice.
When something in the code is referenced to as a "Nowiki Tag", it means a tag
which causes wiki text to not be processed, which includes <nowiki>, <pre>,
and <syntaxhighlight>
Since we only care about these tags, we can ignore the idea of an intercepting
tag preventing processing, and just go straight for the first ending we can find
If there is no ending to find, the tag will NOT consume the rest of the text in
terms of processing behaviour (though <pre> will appear to have an effect).
Even if there is no end of the tag, the content inside the opening half will
still be unprocessed, meaning {{X20|<nowiki }}>}} wouldn't end at the first }}
despite there being no ending to the tag.
Note that there are some tags, like <math>, which also function like <nowiki>
which are included in this aswell. Some other tags, like <ref>, have far too
unpredictable behaviour to be handled currently (they'd have to be split and
processed as something seperate - its complicated, but maybe not impossible.)
I suspect that every tag listed in [[Special:Version]] may behave somewhat like
this, but that's far too many cases worth checking for rarely used tags that may
not even have a good reason to contain {{ or }} anyways, so we leave them alone.
---- HTML COMMENTS AND INCLUDEONLY ----
HTML Comments are about as basic as it could get for this
Start at <!--, end at -->, no extra conditions. Simple enough
If a comment has no end, it will eat all text instead of not being processed
includeonly tags function mostly like a regular nowiki tag, with the exception
that the tag will actually consume all future text if not given an ending as
opposed to simply giving up and not changing anything. Due to complications and
the fact that this is far less likely to be present on a page, aswell as being
something that may not want to be escaped, includeonly tags are ignored during
our processing
--]=]
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}
--This function expects the string to start with the tag
local function TestForNowikiTag(text, scanPosition)
local tagName = (string.match(text, "^<([^\n />]+)", scanPosition) or ""):lower()
if not validtags[tagName] then
return nil
end
local nextOpener = string.find(text, "<", scanPosition+1) or -1
local nextCloser = string.find(text, ">", scanPosition+1) or -1
if nextCloser > -1 and (nextOpener == -1 or nextCloser < nextOpener) then
local startingTag = string.sub(text, scanPosition, nextCloser)
--We have our starting tag (E.g. '<pre style="color:red">')
--Now find our ending...
if endswith(startingTag, "/>") then --self-closing tag (we are our own ending)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
else
local endingTagStart, endingTagEnd = string.find(text, "</"..allcases(tagName).."[ \t\n]*>", scanPosition)
if endingTagStart then --Regular tag formation
local endingTag = string.sub(text, endingTagStart, endingTagEnd)
local tagContent = string.sub(text, nextCloser+1, endingTagStart-1)
return {
Tag = tagName,
Start = startingTag,
Content = tagContent,
End = endingTag,
Length = #startingTag + #tagContent + #endingTag
}
else --Content inside still needs escaping (also linter error!)
return {
Tag = tagName,
Start = startingTag,
Content = "", End = "",
Length = #startingTag
}
end
end
end
return nil
end
local function TestForComment(text, scanPosition) --Like TestForNowikiTag but for <!-- -->
if string.match(text, "^<!%-%-", scanPosition) then
local commentEnd = string.find(text, "-->", scanPosition+4, true)
if commentEnd then
return {
Start = "<!--", End = "-->",
Content = string.sub(text, scanPosition+4, commentEnd-1),
Length = commentEnd-scanPosition+3
}
else --Consumes all text if not given an ending
return {
Start = "<!--", End = "",
Content = string.sub(text, scanPosition+4),
Length = #text-scanPosition+1
}
end
end
return nil
end
--[[ Implementation notes
The goal of this function is to escape all text that wouldn't be parsed if it
was preprocessed (see above implementation notes).
Using keepComments will keep all HTML comments instead of removing them. They
will still be escaped regardless to avoid processing errors
--]]
local function PrepareText(text, keepComments)
local newtext = {}
local scanPosition = 1
while true do
local NextCheck = string.find(text, "<[NnSsPpMm!]", scanPosition) --Advance to the next potential tag we care about
if not NextCheck then --Done
newtext[#newtext+1] = string.sub(text,scanPosition)
break
end
newtext[#newtext+1] = string.sub(text,scanPosition,NextCheck-1)
scanPosition = NextCheck
local Comment = TestForComment(text, scanPosition)
if Comment then
if keepComments then
newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End
end
scanPosition = scanPosition + Comment.Length
else
local Tag = TestForNowikiTag(text, scanPosition)
if Tag then
local newTagStart = "<" .. mw.text.nowiki(string.sub(Tag.Start,2,-2)) .. ">"
local newTagEnd =
Tag.End == "" and "" or --Respect no tag ending
"</" .. mw.text.nowiki(string.sub(Tag.End,3,-2)) .. ">"
local newContent = mw.text.nowiki(Tag.Content)
newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd
scanPosition = scanPosition + Tag.Length
else --Nothing special, move on...
newtext[#newtext+1] = string.sub(text, scanPosition, scanPosition)
scanPosition = scanPosition + 1
end
end
end
return table.concat(newtext, "")
end
--[=[ Implementation notes
This function is an alternative to Transcluder's getParameters which considers
the potential for a singular { or } or other odd syntax that %b doesn't like to
be in a parameter's value.
When handling the difference between {{ and {{{, mediawiki will attempt to match
as many sequences of {{{ as possible before matching a {{
E.g.
{{{{A}}}} -> { {{{A}}} }
{{{{{{{{Text|A}}}}}}}} -> {{ {{{ {{{Text|A}}} }}} }}
If there aren't enough triple braces on both sides, the parser will compromise
for a template interpretation.
E.g.
{{{{A}} }} -> {{ {{ A }} }}
While there are technically concerns about things such as wikilinks breaking
template processing (E.g. {{[[}}]]}} doesn't stop at the first }}), it shouldn't
be our job to process inputs perfectly when the input has garbage ({ / } isn't
legal in titles anyways, so if something's unmatched in a wikilink, it's
guaranteed GIGO)
Setting dontEscape will prevent running the input text through EET. Avoid
setting this to true if you don't have to set it.
Returned values:
A table of all templates. Template data goes as follows:
Text: The raw text of the template
Name: The name of the template
Args: A list of arguments
Children: A list of immediate template children
--]=]
--Helper functions
local function boundlen(pair)
return pair.End-pair.Start+1
end
--Main function
local function ParseTemplates(InputText, dontEscape)
--Setup
if not dontEscape then
InputText = PrepareText(InputText)
end
local function finalise(text)
if not dontEscape then
return mw.text.decode(text)
else
return text
end
end
local function CreateContainerObj(Container)
Container.Text = {}
Container.Args = {}
Container.ArgOrder = {}
Container.Children = {}
-- Container.Name = nil
-- Container.Value = nil
-- Container.Key = nil
Container.BeyondStart = false
Container.LastIndex = 1
Container.finalise = finalise
function Container:HandleArgInput(character, internalcall)
if not internalcall then
self.Text[#self.Text+1] = character
end
if character == "=" then
if self.Key then
self.Value[#self.Value+1] = character
else
self.Key = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = {}
end
else --"|" or "}"
if not self.Name then
self.Name = cheaptrim(self.Value and table.concat(self.Value, "") or "")
self.Value = nil
else
self.Value = self.finalise(self.Value and table.concat(self.Value, "") or "")
if self.Key then
self.Key = self.finalise(self.Key)
self.Args[self.Key] = cheaptrim(self.Value)
self.ArgOrder[#self.ArgOrder+1] = self.Key
else
local Key = tostring(self.LastIndex)
self.Args[Key] = self.Value
self.ArgOrder[#self.ArgOrder+1] = Key
self.LastIndex = self.LastIndex + 1
end
self.Key = nil
self.Value = nil
end
end
end
function Container:AppendText(text, ftext)
self.Text[#self.Text+1] = (ftext or text)
if not self.Value then
self.Value = {}
end
self.BeyondStart = self.BeyondStart or (#table.concat(self.Text, "") > 2)
if self.BeyondStart then
self.Value[#self.Value+1] = text
end
end
function Container:Clean(IsTemplate)
self.Text = table.concat(self.Text, "")
if self.Value and IsTemplate then
self.Value = {string.sub(table.concat(self.Value, ""), 1, -3)} --Trim ending }}
self:HandleArgInput("|", true) --Simulate ending
end
self.Value = nil
self.Key = nil
self.BeyondStart = nil
self.LastIndex = nil
self.finalise = nil
self.HandleArgInput = nil
self.AppendText = nil
self.Clean = nil
end
return Container
end
--Step 1: Find and escape the content of all wikilinks on the page, which are stronger than templates (see implementation notes)
local scannerPosition = 1
local wikilinks = {}
local openWikilinks = {}
while true do
local Position, _, Character = string.find(InputText, "([%[%]])%1", scannerPosition)
if not Position then --Done
break
end
scannerPosition = Position+2 --+2 to pass the [[ / ]]
if Character == "[" then --Add a [[ to the pending wikilink queue
openWikilinks[#openWikilinks+1] = Position
else --Pair up the ]] to any available [[
if #openWikilinks >= 1 then
local start = table.remove(openWikilinks) --Pop the latest [[
wikilinks[start] = {Start=start, End=Position+1, Type="Wikilink"} --Note the pair
end
end
end
--Step 2: Find the bounds of every valid template and variable ({{ and {{{)
local scannerPosition = 1
local templates = {}
local variables = {}
local openBrackets = {}
while true do
local Start, _, Character = string.find(InputText, "([{}])%1", scannerPosition)
if not Start then --Done (both 9e9)
break
end
local _, End = string.find(InputText, "^"..Character.."+", Start)
scannerPosition = Start --Get to the {{ / }} set
if Character == "{" then --Add the {{+ set to the queue
openBrackets[#openBrackets+1] = {Start=Start, End=End}
else --Pair up the }} to any available {{, accounting for {{{ / }}}
local BracketCount = End-Start+1
while BracketCount >= 2 and #openBrackets >= 1 do
local OpenSet = table.remove(openBrackets)
if boundlen(OpenSet) >= 3 and BracketCount >= 3 then --We have a {{{variable}}} (both sides have 3 spare)
variables[OpenSet.End-2] = {Start=OpenSet.End-2, End=scannerPosition+2, Type="Variable"} --Done like this to ensure chronological order
BracketCount = BracketCount - 3
OpenSet.End = OpenSet.End - 3
scannerPosition = scannerPosition + 3
else --We have a {{template}} (both sides have 2 spare, but at least one side doesn't have 3 spare)
templates[OpenSet.End-1] = {Start=OpenSet.End-1, End=scannerPosition+1, Type="Template"} --Done like this to ensure chronological order
BracketCount = BracketCount - 2
OpenSet.End = OpenSet.End - 2
scannerPosition = scannerPosition + 2
end
if boundlen(OpenSet) >= 2 then --Still has enough data left, leave it in
openBrackets[#openBrackets+1] = OpenSet
end
end
end
scannerPosition = End --Now move past the bracket set
end
--Step 3: Re-trace every object using their known bounds, collecting our parameters with (slight) ease
local scannerPosition = 1
local activeObjects = {}
local finalObjects = {}
while true do
local LatestObject = activeObjects[#activeObjects] --Commonly needed object
local NNC, _, Character --NNC = NextNotableCharacter
if LatestObject then
NNC, _, Character = string.find(InputText, "([{}%[%]|=])", scannerPosition)
else
NNC, _, Character = string.find(InputText, "([{}])", scannerPosition) --We are only after templates right now
end
if not NNC then
break
end
if NNC > scannerPosition and LatestObject then
local scannedContent = string.sub(InputText, scannerPosition, NNC-1)
LatestObject:AppendText(scannedContent, finalise(scannedContent))
end
scannerPosition = NNC+1
if Character == "{" or Character == "[" then
local Container = templates[NNC] or variables[NNC] or wikilinks[NNC]
if Container then
CreateContainerObj(Container)
if Container.Type == "Template" then
Container:AppendText("{{")
scannerPosition = NNC+2
elseif Container.Type == "Variable" then
Container:AppendText("{{{")
scannerPosition = NNC+3
else --Wikilink
Container:AppendText("[[")
scannerPosition = NNC+2
end
if LatestObject and Container.Type == "Template" then --Only templates count as children
LatestObject.Children[#LatestObject.Children+1] = Container
end
activeObjects[#activeObjects+1] = Container
elseif LatestObject then
LatestObject:AppendText(Character)
end
elseif Character == "}" or Character == "]" then
if LatestObject then
LatestObject:AppendText(Character)
if LatestObject.End == NNC then
if LatestObject.Type == "Template" then
LatestObject:Clean(true)
finalObjects[#finalObjects+1] = LatestObject
else
LatestObject:Clean(false)
end
activeObjects[#activeObjects] = nil
local NewLatest = activeObjects[#activeObjects]
if NewLatest then
NewLatest:AppendText(LatestObject.Text) --Append to new latest
end
end
end
else --| or =
if LatestObject then
LatestObject:HandleArgInput(Character)
end
end
end
--Step 4: Fix the order
local FixedOrder = {}
local SortableReference = {}
for _,Object in next,finalObjects do
SortableReference[#SortableReference+1] = Object.Start
end
table.sort(SortableReference)
for i = 1,#SortableReference do
local start = SortableReference[i]
for n,Object in next,finalObjects do
if Object.Start == start then
finalObjects[n] = nil
Object.Start = nil --Final cleanup
Object.End = nil
Object.Type = nil
FixedOrder[#FixedOrder+1] = Object
break
end
end
end
--Finished, return
return FixedOrder
end
local p = {}
--Main entry points
p.PrepareText = PrepareText
p.ParseTemplates = ParseTemplates
--Extra entry points, not really required
p.TestForNowikiTag = TestForNowikiTag
p.TestForComment = TestForComment
return p
--[==[ console tests
local s = [=[Hey!{{Text|<nowiki | ||>
Hey! }}
A</nowiki>|<!--AAAAA|AAA-->Should see|Shouldn't see}}]=]
local out = p.PrepareText(s)
mw.logObject(out)
local s = [=[B<!--
Hey!
-->A]=]
local out = p.TestForComment(s, 2)
mw.logObject(out); mw.log(string.sub(s, 2, out.Length))
local a = p.ParseTemplates([=[
{{User:Aidan9382/templates/dummy
|A|B|C {{{A|B}}} { } } {
|<nowiki>D</nowiki>
|<pre>E
|F</pre>
|G|=|a=|A = [[{{PAGENAME}}|A=B]]{{Text|1==<nowiki>}}</nowiki>}}|A B=Success}}
]=])
mw.logObject(a)
]==]
1pc5nwfjm9r5mkdrg3s2ci608uixfb7
Module:Pagetype/softredirect
828
85278
281605
2024-01-17T20:24:55Z
en>MSGJ
0
create
281605
Scribunto
text/plain
-- This page contains a table of all soft redirect templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
["Soft redirect"] = true,
["Interwiki redirect"] = true,
["SoftRedirect"] = true,
["Soft Redirect"] = true,
["Softredirect"] = true,
["Softredir"] = true,
["Soft link"] = true,
["Soft redir"] = true,
["Soft"] = true,
["Plain soft redirect"] = true,
["Softr"] = true
}
31q8hk3h8rrwj0kx7wta4z9gbs8rbyd
281606
281605
2024-01-17T20:29:11Z
en>MSGJ
0
add Wiktionary redirect
281606
Scribunto
text/plain
-- This page contains a table of all soft redirect templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
["Soft redirect"] = true,
["Interwiki redirect"] = true,
["SoftRedirect"] = true,
["Soft Redirect"] = true,
["Softredirect"] = true,
["Softredir"] = true,
["Soft link"] = true,
["Soft redir"] = true,
["Soft"] = true,
["Plain soft redirect"] = true,
["Softr"] = true,
["Wiktionary redirect"] = true,
["Moved to Wiktionary"] = true,
["RedirecttoWiktionary"] = true,
["Wi"] = true,
["Wiktionary Redirect"] = true,
["Wtr"] = true,
["Wtsr"] = true,
["Wiktionaryredirect"] = true,
["WiktionaryRedirect"] = true,
["Wiktionary-redirect"] = true,
["Wiktredir"] = true,
["Wiktr"] = true,
["Wikt redirect"] = true,
["Wikt red"] = true,
["Wiktred"] = true,
}
nbk7k5o70sjj4fk9t2u4y2q4lmkhpmd
281607
281606
2024-01-17T20:34:35Z
en>MSGJ
0
add Wikibooks redirect
281607
Scribunto
text/plain
-- This page contains a table of all soft redirect templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
["Soft redirect"] = true,
["Interwiki redirect"] = true,
["SoftRedirect"] = true,
["Soft Redirect"] = true,
["Softredirect"] = true,
["Softredir"] = true,
["Soft link"] = true,
["Soft redir"] = true,
["Soft"] = true,
["Plain soft redirect"] = true,
["Softr"] = true,
["Wiktionary redirect"] = true,
["Moved to Wiktionary"] = true,
["RedirecttoWiktionary"] = true,
["Wi"] = true,
["Wiktionary Redirect"] = true,
["Wtr"] = true,
["Wtsr"] = true,
["Wiktionaryredirect"] = true,
["WiktionaryRedirect"] = true,
["Wiktionary-redirect"] = true,
["Wiktredir"] = true,
["Wiktr"] = true,
["Wikt redirect"] = true,
["Wikt red"] = true,
["Wiktred"] = true,
["Wikibooks redirect"] = true,
["WBOOK"] = true,
["Wbook"] = true,
["Wikibooks Redirect"] = true,
["WikibooksRedirect"] = true,
["Wikibooksredirect"] = true,
["Wikibook redirect"] = true,
}
de066vreok9yorphup8vl1wgurjth5z
281608
281607
2024-01-17T20:35:42Z
en>MSGJ
0
+Wikiquote redirect
281608
Scribunto
text/plain
-- This page contains a table of all soft redirect templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
["Soft redirect"] = true,
["Interwiki redirect"] = true,
["SoftRedirect"] = true,
["Soft Redirect"] = true,
["Softredirect"] = true,
["Softredir"] = true,
["Soft link"] = true,
["Soft redir"] = true,
["Soft"] = true,
["Plain soft redirect"] = true,
["Softr"] = true,
["Wiktionary redirect"] = true,
["Moved to Wiktionary"] = true,
["RedirecttoWiktionary"] = true,
["Wi"] = true,
["Wiktionary Redirect"] = true,
["Wtr"] = true,
["Wtsr"] = true,
["Wiktionaryredirect"] = true,
["WiktionaryRedirect"] = true,
["Wiktionary-redirect"] = true,
["Wiktredir"] = true,
["Wiktr"] = true,
["Wikt redirect"] = true,
["Wikt red"] = true,
["Wiktred"] = true,
["Wikibooks redirect"] = true,
["WBOOK"] = true,
["Wbook"] = true,
["Wikibooks Redirect"] = true,
["WikibooksRedirect"] = true,
["Wikibooksredirect"] = true,
["Wikibook redirect"] = true,
["Wikiquote redirect"] = true,
["Wq"] = true,
}
e9anvkuyev4p0wdc2c8cg8t10kx6xt6
281609
281608
2024-01-17T20:43:57Z
en>MSGJ
0
add more
281609
Scribunto
text/plain
-- This page contains a table of all soft redirect templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
["Soft redirect"] = true,
["Interwiki redirect"] = true,
["SoftRedirect"] = true,
["Soft Redirect"] = true,
["Softredirect"] = true,
["Softredir"] = true,
["Soft link"] = true,
["Soft redir"] = true,
["Soft"] = true,
["Plain soft redirect"] = true,
["Softr"] = true,
["Wiktionary redirect"] = true,
["Moved to Wiktionary"] = true,
["RedirecttoWiktionary"] = true,
["Wi"] = true,
["Wiktionary Redirect"] = true,
["Wtr"] = true,
["Wtsr"] = true,
["Wiktionaryredirect"] = true,
["WiktionaryRedirect"] = true,
["Wiktionary-redirect"] = true,
["Wiktredir"] = true,
["Wiktr"] = true,
["Wikt redirect"] = true,
["Wikt red"] = true,
["Wiktred"] = true,
["Wikibooks redirect"] = true,
["WBOOK"] = true,
["Wbook"] = true,
["Wikibooks Redirect"] = true,
["WikibooksRedirect"] = true,
["Wikibooksredirect"] = true,
["Wikibook redirect"] = true,
["Wikiquote redirect"] = true,
["Wq"] = true,
["Wikisource redirect"] = true,
["Wikispecies redirect"] = true,
["WSPEC"] = true,
["Wspec"] = true,
["Wikispecies Redirect"] = true,
["WikispeciesRedirect"] = true,
["Wikispeciesredirect"] = true,
["Wikivoyage redirect"] = true,
["Wikimedia Commons redirect"] = true,
["COMM"] = true,
["Commons redirect"] = true,
["Comm"] = true,
["Commonsredirect"] = true,
["Commons Redirect"] = true,
["CommonsRedirect"] = true,
["Wikimedia commons redirect"] = true,
}
syqcyex8lttbep8em9c3bqtjldh3jgs
281610
281609
2024-01-17T20:45:10Z
en>MSGJ
0
+Soft redirect with Wikidata item
281610
Scribunto
text/plain
-- This page contains a table of all soft redirect templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
["Soft redirect"] = true,
["Interwiki redirect"] = true,
["SoftRedirect"] = true,
["Soft Redirect"] = true,
["Softredirect"] = true,
["Softredir"] = true,
["Soft link"] = true,
["Soft redir"] = true,
["Soft"] = true,
["Plain soft redirect"] = true,
["Softr"] = true,
["Wiktionary redirect"] = true,
["Moved to Wiktionary"] = true,
["RedirecttoWiktionary"] = true,
["Wi"] = true,
["Wiktionary Redirect"] = true,
["Wtr"] = true,
["Wtsr"] = true,
["Wiktionaryredirect"] = true,
["WiktionaryRedirect"] = true,
["Wiktionary-redirect"] = true,
["Wiktredir"] = true,
["Wiktr"] = true,
["Wikt redirect"] = true,
["Wikt red"] = true,
["Wiktred"] = true,
["Wikibooks redirect"] = true,
["WBOOK"] = true,
["Wbook"] = true,
["Wikibooks Redirect"] = true,
["WikibooksRedirect"] = true,
["Wikibooksredirect"] = true,
["Wikibook redirect"] = true,
["Wikiquote redirect"] = true,
["Wq"] = true,
["Wikisource redirect"] = true,
["Wikispecies redirect"] = true,
["WSPEC"] = true,
["Wspec"] = true,
["Wikispecies Redirect"] = true,
["WikispeciesRedirect"] = true,
["Wikispeciesredirect"] = true,
["Wikivoyage redirect"] = true,
["Wikimedia Commons redirect"] = true,
["COMM"] = true,
["Commons redirect"] = true,
["Comm"] = true,
["Commonsredirect"] = true,
["Commons Redirect"] = true,
["CommonsRedirect"] = true,
["Wikimedia commons redirect"] = true,
["Soft redirect with Wikidata item"] = true,
["Wikidata-redirect"] = true,
["Wdr"] = true,
["Wikidata redirect"] = true
}
ibilrgb0nsv9cp5q6x1gcgcinuh4vqa
281611
281610
2024-01-31T19:55:47Z
en>MSGJ
0
Protected "[[Module:Pagetype/softredirect]]": [[WP:High-risk templates|High-risk template or module]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
281610
Scribunto
text/plain
-- This page contains a table of all soft redirect templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
["Soft redirect"] = true,
["Interwiki redirect"] = true,
["SoftRedirect"] = true,
["Soft Redirect"] = true,
["Softredirect"] = true,
["Softredir"] = true,
["Soft link"] = true,
["Soft redir"] = true,
["Soft"] = true,
["Plain soft redirect"] = true,
["Softr"] = true,
["Wiktionary redirect"] = true,
["Moved to Wiktionary"] = true,
["RedirecttoWiktionary"] = true,
["Wi"] = true,
["Wiktionary Redirect"] = true,
["Wtr"] = true,
["Wtsr"] = true,
["Wiktionaryredirect"] = true,
["WiktionaryRedirect"] = true,
["Wiktionary-redirect"] = true,
["Wiktredir"] = true,
["Wiktr"] = true,
["Wikt redirect"] = true,
["Wikt red"] = true,
["Wiktred"] = true,
["Wikibooks redirect"] = true,
["WBOOK"] = true,
["Wbook"] = true,
["Wikibooks Redirect"] = true,
["WikibooksRedirect"] = true,
["Wikibooksredirect"] = true,
["Wikibook redirect"] = true,
["Wikiquote redirect"] = true,
["Wq"] = true,
["Wikisource redirect"] = true,
["Wikispecies redirect"] = true,
["WSPEC"] = true,
["Wspec"] = true,
["Wikispecies Redirect"] = true,
["WikispeciesRedirect"] = true,
["Wikispeciesredirect"] = true,
["Wikivoyage redirect"] = true,
["Wikimedia Commons redirect"] = true,
["COMM"] = true,
["Commons redirect"] = true,
["Comm"] = true,
["Commonsredirect"] = true,
["Commons Redirect"] = true,
["CommonsRedirect"] = true,
["Wikimedia commons redirect"] = true,
["Soft redirect with Wikidata item"] = true,
["Wikidata-redirect"] = true,
["Wdr"] = true,
["Wikidata redirect"] = true
}
ibilrgb0nsv9cp5q6x1gcgcinuh4vqa
281612
281611
2024-02-17T03:50:40Z
en>Pppery
0
Changed protection settings for "[[Module:Pagetype/softredirect]]": Per parent ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite))
281610
Scribunto
text/plain
-- This page contains a table of all soft redirect templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
["Soft redirect"] = true,
["Interwiki redirect"] = true,
["SoftRedirect"] = true,
["Soft Redirect"] = true,
["Softredirect"] = true,
["Softredir"] = true,
["Soft link"] = true,
["Soft redir"] = true,
["Soft"] = true,
["Plain soft redirect"] = true,
["Softr"] = true,
["Wiktionary redirect"] = true,
["Moved to Wiktionary"] = true,
["RedirecttoWiktionary"] = true,
["Wi"] = true,
["Wiktionary Redirect"] = true,
["Wtr"] = true,
["Wtsr"] = true,
["Wiktionaryredirect"] = true,
["WiktionaryRedirect"] = true,
["Wiktionary-redirect"] = true,
["Wiktredir"] = true,
["Wiktr"] = true,
["Wikt redirect"] = true,
["Wikt red"] = true,
["Wiktred"] = true,
["Wikibooks redirect"] = true,
["WBOOK"] = true,
["Wbook"] = true,
["Wikibooks Redirect"] = true,
["WikibooksRedirect"] = true,
["Wikibooksredirect"] = true,
["Wikibook redirect"] = true,
["Wikiquote redirect"] = true,
["Wq"] = true,
["Wikisource redirect"] = true,
["Wikispecies redirect"] = true,
["WSPEC"] = true,
["Wspec"] = true,
["Wikispecies Redirect"] = true,
["WikispeciesRedirect"] = true,
["Wikispeciesredirect"] = true,
["Wikivoyage redirect"] = true,
["Wikimedia Commons redirect"] = true,
["COMM"] = true,
["Commons redirect"] = true,
["Comm"] = true,
["Commonsredirect"] = true,
["Commons Redirect"] = true,
["CommonsRedirect"] = true,
["Wikimedia commons redirect"] = true,
["Soft redirect with Wikidata item"] = true,
["Wikidata-redirect"] = true,
["Wdr"] = true,
["Wikidata redirect"] = true
}
ibilrgb0nsv9cp5q6x1gcgcinuh4vqa
281613
281612
2024-08-08T21:25:34Z
en>MSGJ
0
update from sandbox as StradBot found lots more of these templates
281613
Scribunto
text/plain
-- This page contains a table of all soft redirect templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
["R from category navigation"] = true,
["Redirect from category navigation"] = true,
["Category redirect"] = true,
["Cat move"] = true,
["Cat red"] = true,
["Cat redir"] = true,
["Cat redirect"] = true,
["Category move"] = true,
["Category Redirect"] = true,
["Categoryredirect"] = true,
["Catr"] = true,
["Catred"] = true,
["Catredir"] = true,
["Catredirect"] = true,
["R from template-generated category"] = true,
["Redirect from template-generated category"] = true,
["Portal soft redirect"] = true,
["Portal Redirect"] = true,
["Portal redirect"] = true,
["Portal Soft Redirect"] = true,
["Portalredirect"] = true,
["Portalsoftredirect"] = true,
["PortRed"] = true,
["Portred"] = true,
["Portsoftred"] = true,
["PSR"] = true,
["Psr"] = true,
["Soft portal redirect"] = true,
["Soft redirect portal"] = true,
["SPR"] = true,
["Spr"] = true,
["SRP"] = true,
["Srp"] = true,
["Salted redirect"] = true,
["Soft redirect"] = true,
["Interwiki redirect"] = true,
["Plain soft redirect"] = true,
["Soft"] = true,
["Soft link"] = true,
["Soft redir"] = true,
["Soft Redirect"] = true,
["Softr"] = true,
["Softredir"] = true,
["SoftRedirect"] = true,
["Softredirect"] = true,
["Userrename"] = true,
["Wikibooks redirect"] = true,
["WBOOK"] = true,
["Wbook"] = true,
["Wikibook redirect"] = true,
["Wikibooks Redirect"] = true,
["WikibooksRedirect"] = true,
["Wikibooksredirect"] = true,
["Wikimedia Commons redirect"] = true,
["COMM"] = true,
["Comm"] = true,
["Commons Redirect"] = true,
["Commons redirect"] = true,
["CommonsRedirect"] = true,
["Commonsredirect"] = true,
["Wikimedia commons redirect"] = true,
["Wikiquote redirect"] = true,
["Wq"] = true,
["Wikisource redirect"] = true,
["Wikispecies redirect"] = true,
["Wikispecies Redirect"] = true,
["WikispeciesRedirect"] = true,
["Wikispeciesredirect"] = true,
["WSPEC"] = true,
["Wspec"] = true,
["Wikivoyage redirect"] = true,
["Wiktionary redirect"] = true,
["Moved to Wiktionary"] = true,
["RedirecttoWiktionary"] = true,
["Wi"] = true,
["Wikt red"] = true,
["Wikt redirect"] = true,
["Wiktionary Redirect"] = true,
["Wiktionary-redirect"] = true,
["WiktionaryRedirect"] = true,
["Wiktionaryredirect"] = true,
["Wiktr"] = true,
["Wiktred"] = true,
["Wiktredir"] = true,
["Wtr"] = true,
["Wtsr"] = true,
}
krad2do3vq17ck777hpwdoaxr3vo5yt
281614
281613
2026-06-07T19:30:43Z
Ameisenigel
44
9 revisions imported from [[:en:Module:Pagetype/softredirect]]: Request at [[WF:AN]]
281613
Scribunto
text/plain
-- This page contains a table of all soft redirect templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
["R from category navigation"] = true,
["Redirect from category navigation"] = true,
["Category redirect"] = true,
["Cat move"] = true,
["Cat red"] = true,
["Cat redir"] = true,
["Cat redirect"] = true,
["Category move"] = true,
["Category Redirect"] = true,
["Categoryredirect"] = true,
["Catr"] = true,
["Catred"] = true,
["Catredir"] = true,
["Catredirect"] = true,
["R from template-generated category"] = true,
["Redirect from template-generated category"] = true,
["Portal soft redirect"] = true,
["Portal Redirect"] = true,
["Portal redirect"] = true,
["Portal Soft Redirect"] = true,
["Portalredirect"] = true,
["Portalsoftredirect"] = true,
["PortRed"] = true,
["Portred"] = true,
["Portsoftred"] = true,
["PSR"] = true,
["Psr"] = true,
["Soft portal redirect"] = true,
["Soft redirect portal"] = true,
["SPR"] = true,
["Spr"] = true,
["SRP"] = true,
["Srp"] = true,
["Salted redirect"] = true,
["Soft redirect"] = true,
["Interwiki redirect"] = true,
["Plain soft redirect"] = true,
["Soft"] = true,
["Soft link"] = true,
["Soft redir"] = true,
["Soft Redirect"] = true,
["Softr"] = true,
["Softredir"] = true,
["SoftRedirect"] = true,
["Softredirect"] = true,
["Userrename"] = true,
["Wikibooks redirect"] = true,
["WBOOK"] = true,
["Wbook"] = true,
["Wikibook redirect"] = true,
["Wikibooks Redirect"] = true,
["WikibooksRedirect"] = true,
["Wikibooksredirect"] = true,
["Wikimedia Commons redirect"] = true,
["COMM"] = true,
["Comm"] = true,
["Commons Redirect"] = true,
["Commons redirect"] = true,
["CommonsRedirect"] = true,
["Commonsredirect"] = true,
["Wikimedia commons redirect"] = true,
["Wikiquote redirect"] = true,
["Wq"] = true,
["Wikisource redirect"] = true,
["Wikispecies redirect"] = true,
["Wikispecies Redirect"] = true,
["WikispeciesRedirect"] = true,
["Wikispeciesredirect"] = true,
["WSPEC"] = true,
["Wspec"] = true,
["Wikivoyage redirect"] = true,
["Wiktionary redirect"] = true,
["Moved to Wiktionary"] = true,
["RedirecttoWiktionary"] = true,
["Wi"] = true,
["Wikt red"] = true,
["Wikt redirect"] = true,
["Wiktionary Redirect"] = true,
["Wiktionary-redirect"] = true,
["WiktionaryRedirect"] = true,
["Wiktionaryredirect"] = true,
["Wiktr"] = true,
["Wiktred"] = true,
["Wiktredir"] = true,
["Wtr"] = true,
["Wtsr"] = true,
}
krad2do3vq17ck777hpwdoaxr3vo5yt
Module:Pagetype/rfd
828
85279
281615
2024-05-06T20:54:27Z
en>Aidan9382
0
rfd associated templates
281615
Scribunto
text/plain
-- This page contains a table of all RfD templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
-- Template forms (these should be substituted so we should rarely see these)
["Redirect for discussion"] = true,
["RFD"] = true,
["RfD"] = true,
["Rfd1"] = true,
["Rfd-t"] = true,
["Rfd"] = true,
-- Module forms
["#invoke:RfD"] = true,
["<includeonly>safesubst:</includeonly>#invoke:RfD"] = true, -- The form made by substituting RfD
}
kqfe53buf876ukagmqaubnzk7q4986o
281616
281615
2024-05-09T08:51:35Z
en>MSGJ
0
Protected "[[Module:Pagetype/rfd]]": [[WP:High-risk templates|High-risk template or module]] ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite))
281615
Scribunto
text/plain
-- This page contains a table of all RfD templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
-- Template forms (these should be substituted so we should rarely see these)
["Redirect for discussion"] = true,
["RFD"] = true,
["RfD"] = true,
["Rfd1"] = true,
["Rfd-t"] = true,
["Rfd"] = true,
-- Module forms
["#invoke:RfD"] = true,
["<includeonly>safesubst:</includeonly>#invoke:RfD"] = true, -- The form made by substituting RfD
}
kqfe53buf876ukagmqaubnzk7q4986o
281617
281616
2024-05-20T13:03:21Z
en>MSGJ
0
+Rfd-NPF, seen on [[West Ardougne]]
281617
Scribunto
text/plain
-- This page contains a table of all RfD templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
-- Template forms (these should be substituted so we should rarely see these)
["Redirect for discussion"] = true,
["RFD"] = true,
["RfD"] = true,
["Rfd1"] = true,
["Rfd-t"] = true,
["Rfd"] = true,
["Rfd-NPF"] = true,
-- Module forms
["#invoke:RfD"] = true,
["<includeonly>safesubst:</includeonly>#invoke:RfD"] = true, -- The form made by substituting RfD
}
0awtueckvk0q3th2c4hvgg60v159f7u
281618
281617
2024-05-20T13:04:06Z
en>MSGJ
0
+Rfd-NPF/core
281618
Scribunto
text/plain
-- This page contains a table of all RfD templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
-- Template forms (these should be substituted so we should rarely see these)
["Redirect for discussion"] = true,
["RFD"] = true,
["RfD"] = true,
["Rfd1"] = true,
["Rfd-t"] = true,
["Rfd"] = true,
["Rfd-NPF"] = true,
["Rfd-NPF/core"] = true,
-- Module forms
["#invoke:RfD"] = true,
["<includeonly>safesubst:</includeonly>#invoke:RfD"] = true, -- The form made by substituting RfD
}
9dx0g2c8g863kg6hcwx1wgsou16k45p
281619
281618
2024-08-20T20:41:41Z
en>Pppery
0
Changed protection settings for "[[Module:Pagetype/rfd]]": The highest-use template on the project that's not currently fully protected ([Edit=Require administrator access] (indefinite) [Move=Require administrator access] (indefinite))
281618
Scribunto
text/plain
-- This page contains a table of all RfD templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
-- Template forms (these should be substituted so we should rarely see these)
["Redirect for discussion"] = true,
["RFD"] = true,
["RfD"] = true,
["Rfd1"] = true,
["Rfd-t"] = true,
["Rfd"] = true,
["Rfd-NPF"] = true,
["Rfd-NPF/core"] = true,
-- Module forms
["#invoke:RfD"] = true,
["<includeonly>safesubst:</includeonly>#invoke:RfD"] = true, -- The form made by substituting RfD
}
9dx0g2c8g863kg6hcwx1wgsou16k45p
281620
281619
2026-06-07T19:31:46Z
Ameisenigel
44
5 revisions imported from [[:en:Module:Pagetype/rfd]]: Request at [[WF:AN]]
281618
Scribunto
text/plain
-- This page contains a table of all RfD templates and their
-- redirects. Templates names are capitalized, and the Template: prefix is
-- removed. Templates are grouped with the main template first, followed by
-- its redirects.
return {
-- Template forms (these should be substituted so we should rarely see these)
["Redirect for discussion"] = true,
["RFD"] = true,
["RfD"] = true,
["Rfd1"] = true,
["Rfd-t"] = true,
["Rfd"] = true,
["Rfd-NPF"] = true,
["Rfd-NPF/core"] = true,
-- Module forms
["#invoke:RfD"] = true,
["<includeonly>safesubst:</includeonly>#invoke:RfD"] = true, -- The form made by substituting RfD
}
9dx0g2c8g863kg6hcwx1wgsou16k45p
Z36172
0
85280
281630
2026-06-07T20:48:00Z
Seller of unexistent friends
85698
281630
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36172"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z10139",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z10139",
"Z10139K1": "ññññññññññññññññññññññññññññññññññññ"
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z866",
"Z866K2": "ññññññññññññññññññññññññññññññññññññ"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
2agcpd7t3aegfckisxvf2osph6g0tt3
281631
281630
2026-06-07T20:48:34Z
Seller of unexistent friends
85698
281631
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36172"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z10139",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z10139",
"Z10139K1": "ññññññññññññññññññññññññññññññññññññ"
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z866",
"Z866K2": "979bbcb5e4f6d37e52f0f41eb66d60b53f64b607"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
4v8rdrtvm3u8m2rxsx1eq2pyhqunq05
Z36173
0
85281
281632
2026-06-07T20:49:32Z
Seller of unexistent friends
85698
281632
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36173"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z10139",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z10139",
"Z10139K1": "嘘吐姫。/無來、花隈千冬、女性1"
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z866",
"Z866K2": "6ea3277e2e0714d945a27138d17a0b454b0e572f"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
jxiht0shl3q0mng03jlitvbg7c01by8
Z36174
0
85282
281634
2026-06-07T23:18:09Z
YoshiRulz
10156
Create function
281634
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36174"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z13518",
"Z17K2": "Z36174K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "n"
}
]
}
}
],
"Z8K2": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z8K3": [
"Z20"
],
"Z8K4": [
"Z14"
],
"Z8K5": "Z36174"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "nth roots of unity as complex128s"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"nth roots of unity as Complex numbers"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
3yg1t5tmoqvjqmhr1k8t1h5rx1vo8em
281647
281634
2026-06-07T23:55:57Z
YoshiRulz
10156
Added Z36180 and Z36181 to the approved list of test cases
281647
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36174"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z13518",
"Z17K2": "Z36174K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "n"
}
]
}
}
],
"Z8K2": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z8K3": [
"Z20",
"Z36180",
"Z36181"
],
"Z8K4": [
"Z14"
],
"Z8K5": "Z36174"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "nth roots of unity as complex128s"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"nth roots of unity as Complex numbers"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
tkl45h0cr14k1s1zl8ces3l6micm31s
281650
281647
2026-06-08T00:12:24Z
YoshiRulz
10156
Added Z36182 and Z36183 to the approved list of implementations
281650
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36174"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z13518",
"Z17K2": "Z36174K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "n"
}
]
}
}
],
"Z8K2": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z8K3": [
"Z20",
"Z36180",
"Z36181"
],
"Z8K4": [
"Z14",
"Z36182",
"Z36183"
],
"Z8K5": "Z36174"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "nth roots of unity as complex128s"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"nth roots of unity as Complex numbers"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
6e795ry46w2spm6euiqhyhv5lnsb8hv
281651
281650
2026-06-08T00:12:37Z
WikiLambda system
3
Updated the implementation list (see [[Help:Wikifunctions/Implementation_ordering_and_choosing|About implementation selection]])
281651
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36174"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z13518",
"Z17K2": "Z36174K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "n"
}
]
}
}
],
"Z8K2": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z8K3": [
"Z20",
"Z36180",
"Z36181"
],
"Z8K4": [
"Z14",
"Z36183",
"Z36182"
],
"Z8K5": "Z36174"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "nth roots of unity as complex128s"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"nth roots of unity as Complex numbers"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
p5x2owlyhazfp8cbxy7ujjbkqshedm1
281653
281651
2026-06-08T00:18:44Z
YoshiRulz
10156
Removed Z36182 from the approved list of implementations
281653
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36174"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": "Z13518",
"Z17K2": "Z36174K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "n"
}
]
}
}
],
"Z8K2": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z8K3": [
"Z20",
"Z36180",
"Z36181"
],
"Z8K4": [
"Z14",
"Z36183"
],
"Z8K5": "Z36174"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "nth roots of unity as complex128s"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"nth roots of unity as Complex numbers"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
4gj7v834u986tjrs7q66wmdcgl9bmxs
Z36175
0
85283
281635
2026-06-07T23:22:58Z
YoshiRulz
10156
Create function
281635
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36175"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z17K2": "Z36175K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "values to check"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z17K2": "Z36175K2",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "reference values"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z20838",
"Z17K2": "Z36175K3",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "epsilon"
}
]
}
}
],
"Z8K2": "Z40",
"Z8K3": [
"Z20"
],
"Z8K4": [
"Z14"
],
"Z8K5": "Z36175"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "same list of complex128s within tolerance"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"equality of complex128 lists within tolerance",
"equivalent list of complex128s within tolerance",
"same list of complex128s within error",
"same list of complex128s within epsilon"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
4rgx4os5q9c1c9igsc4ah7nxur2ir0t
281637
281635
2026-06-07T23:34:00Z
YoshiRulz
10156
Added Z36176 to the approved list of test cases
281637
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36175"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z17K2": "Z36175K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "values to check"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z17K2": "Z36175K2",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "reference values"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z20838",
"Z17K2": "Z36175K3",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "epsilon"
}
]
}
}
],
"Z8K2": "Z40",
"Z8K3": [
"Z20",
"Z36176"
],
"Z8K4": [
"Z14"
],
"Z8K5": "Z36175"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "same list of complex128s within tolerance"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"equality of complex128 lists within tolerance",
"equivalent list of complex128s within tolerance",
"same list of complex128s within error",
"same list of complex128s within epsilon"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
ixr6q0qakal7pj9756xn1xondzsa60m
281640
281637
2026-06-07T23:41:59Z
YoshiRulz
10156
Added Z36177 and Z36178 to the approved list of implementations
281640
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36175"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z17K2": "Z36175K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "values to check"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z17K2": "Z36175K2",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "reference values"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z20838",
"Z17K2": "Z36175K3",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "epsilon"
}
]
}
}
],
"Z8K2": "Z40",
"Z8K3": [
"Z20",
"Z36176"
],
"Z8K4": [
"Z14",
"Z36177",
"Z36178"
],
"Z8K5": "Z36175"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "same list of complex128s within tolerance"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"equality of complex128 lists within tolerance",
"equivalent list of complex128s within tolerance",
"same list of complex128s within error",
"same list of complex128s within epsilon"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
b00zx37f0nvqxpcd72vwndh93jmce47
281641
281640
2026-06-07T23:42:08Z
WikiLambda system
3
Updated the implementation list (see [[Help:Wikifunctions/Implementation_ordering_and_choosing|About implementation selection]])
281641
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36175"
},
"Z2K2": {
"Z1K1": "Z8",
"Z8K1": [
"Z17",
{
"Z1K1": "Z17",
"Z17K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z17K2": "Z36175K1",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "values to check"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": {
"Z1K1": "Z7",
"Z7K1": "Z881",
"Z881K1": "Z33198"
},
"Z17K2": "Z36175K2",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "reference values"
}
]
}
},
{
"Z1K1": "Z17",
"Z17K1": "Z20838",
"Z17K2": "Z36175K3",
"Z17K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "epsilon"
}
]
}
}
],
"Z8K2": "Z40",
"Z8K3": [
"Z20",
"Z36176"
],
"Z8K4": [
"Z14",
"Z36178",
"Z36177"
],
"Z8K5": "Z36175"
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "same list of complex128s within tolerance"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31",
{
"Z1K1": "Z31",
"Z31K1": "Z1002",
"Z31K2": [
"Z6",
"equality of complex128 lists within tolerance",
"equivalent list of complex128s within tolerance",
"same list of complex128s within error",
"same list of complex128s within epsilon"
]
}
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
adzny8dfr6fd7aibzra2tx30crqi0ex
Z36176
0
85284
281636
2026-06-07T23:33:41Z
YoshiRulz
10156
Create test
281636
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36176"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z36175",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z36175",
"Z36175K1": [
"Z33198",
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "3602879701896397"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "450359962737050"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "2251799813685248"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "3940649673949184"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
}
],
"Z36175K2": [
"Z33198",
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16661"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "0"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "2251799813685248"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "2"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
}
],
"Z36175K3": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z844",
"Z844K2": {
"Z1K1": "Z40",
"Z40K1": "Z41"
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "[ 0.9+2.2i, 3.0+3.75i ] ≈ [ 1.0+2.0i, 3.0+4.0i ]"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
nfnyuz4vrsl5onxwm0qm83ka2f2cd8h
Z36177
0
85285
281638
2026-06-07T23:35:57Z
YoshiRulz
10156
Create implementation
281638
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36177"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z36175",
"Z14K2": {
"Z1K1": "Z7",
"Z7K1": "Z12684",
"Z12684K1": {
"Z1K1": "Z7",
"Z7K1": "Z31095",
"Z31095K1": "Z33506",
"Z31095K2": {
"Z1K1": "Z18",
"Z18K1": "Z36175K1"
},
"Z31095K3": {
"Z1K1": "Z18",
"Z18K1": "Z36175K2"
},
"Z31095K4": {
"Z1K1": "Z18",
"Z18K1": "Z36175K3"
}
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "same complex128 list within error, map composition"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
6ml64b89qlja2wjagtbs3xcwk86vg70
Z36178
0
85286
281639
2026-06-07T23:41:38Z
YoshiRulz
10156
Create implementation
281639
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36178"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z36175",
"Z14K2": {
"Z1K1": "Z7",
"Z7K1": "Z802",
"Z802K1": {
"Z1K1": "Z7",
"Z7K1": "Z12864",
"Z12864K1": {
"Z1K1": "Z18",
"Z18K1": "Z36175K1"
},
"Z12864K2": {
"Z1K1": "Z18",
"Z18K1": "Z36175K2"
}
},
"Z802K2": {
"Z1K1": "Z7",
"Z7K1": "Z10184",
"Z10184K1": {
"Z1K1": "Z7",
"Z7K1": "Z813",
"Z813K1": {
"Z1K1": "Z18",
"Z18K1": "Z36175K1"
}
},
"Z10184K2": {
"Z1K1": "Z7",
"Z7K1": "Z10174",
"Z10174K1": {
"Z1K1": "Z7",
"Z7K1": "Z33506",
"Z33506K1": {
"Z1K1": "Z7",
"Z7K1": "Z811",
"Z811K1": {
"Z1K1": "Z18",
"Z18K1": "Z36175K1"
}
},
"Z33506K2": {
"Z1K1": "Z7",
"Z7K1": "Z811",
"Z811K1": {
"Z1K1": "Z18",
"Z18K1": "Z36175K2"
}
},
"Z33506K3": {
"Z1K1": "Z18",
"Z18K1": "Z36175K3"
}
},
"Z10174K2": {
"Z1K1": "Z7",
"Z7K1": "Z36175",
"Z36175K1": {
"Z1K1": "Z7",
"Z7K1": "Z812",
"Z812K1": {
"Z1K1": "Z18",
"Z18K1": "Z36175K1"
}
},
"Z36175K2": {
"Z1K1": "Z7",
"Z7K1": "Z812",
"Z812K1": {
"Z1K1": "Z18",
"Z18K1": "Z36175K2"
}
},
"Z36175K3": {
"Z1K1": "Z18",
"Z18K1": "Z36175K3"
}
}
}
},
"Z802K3": {
"Z1K1": "Z7",
"Z7K1": "Z851",
"Z851K1": "Z35990",
"Z851K2": [
"Z1"
]
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "same complexes within error, recursive composition"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
ec8kttu40i7eh6lltope227e5z9zmt2
Z36179
0
85287
281642
2026-06-07T23:48:53Z
YoshiRulz
10156
Create implementation
281642
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36179"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z31093",
"Z14K2": {
"Z1K1": "Z7",
"Z7K1": "Z802",
"Z802K1": {
"Z1K1": "Z7",
"Z7K1": "Z12864",
"Z12864K1": {
"Z1K1": "Z18",
"Z18K1": "Z31093K1"
},
"Z12864K2": {
"Z1K1": "Z18",
"Z18K1": "Z31093K2"
}
},
"Z802K2": {
"Z1K1": "Z7",
"Z7K1": "Z10184",
"Z10184K1": {
"Z1K1": "Z7",
"Z7K1": "Z813",
"Z813K1": {
"Z1K1": "Z18",
"Z18K1": "Z31093K1"
}
},
"Z10184K2": {
"Z1K1": "Z7",
"Z7K1": "Z10174",
"Z10174K1": {
"Z1K1": "Z7",
"Z7K1": "Z31090",
"Z31090K1": {
"Z1K1": "Z7",
"Z7K1": "Z811",
"Z811K1": {
"Z1K1": "Z18",
"Z18K1": "Z31093K1"
}
},
"Z31090K2": {
"Z1K1": "Z7",
"Z7K1": "Z811",
"Z811K1": {
"Z1K1": "Z18",
"Z18K1": "Z31093K2"
}
},
"Z31090K3": {
"Z1K1": "Z18",
"Z18K1": "Z31093K3"
}
},
"Z10174K2": {
"Z1K1": "Z7",
"Z7K1": "Z31093",
"Z31093K1": {
"Z1K1": "Z7",
"Z7K1": "Z812",
"Z812K1": {
"Z1K1": "Z18",
"Z18K1": "Z31093K1"
}
},
"Z31093K2": {
"Z1K1": "Z7",
"Z7K1": "Z812",
"Z812K1": {
"Z1K1": "Z18",
"Z18K1": "Z31093K2"
}
},
"Z31093K3": {
"Z1K1": "Z18",
"Z18K1": "Z31093K3"
}
}
}
},
"Z802K3": {
"Z1K1": "Z7",
"Z7K1": "Z851",
"Z851K1": "Z35990",
"Z851K2": [
"Z1"
]
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "same float64s within error, recursive composition"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
78bll3zq6v2phiz4di7hdxs38zvzjlv
Z36180
0
85288
281645
2026-06-07T23:52:37Z
YoshiRulz
10156
Create test
281645
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36180"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z36174",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z36174",
"Z36174K1": {
"Z1K1": "Z13518",
"Z13518K1": "4"
}
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z36175",
"Z36175K2": [
"Z33198",
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16661"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "0"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1023"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20829"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1023"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20829"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16661"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "0"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16661"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "0"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1023"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20829"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1023"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20829"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16661"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "0"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
}
],
"Z36175K3": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "20"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "218766855499149"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "4th roots of unity"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
97mzzlxmsi4zrc65ca5wo9u3t1awzqg
Z36181
0
85289
281646
2026-06-07T23:55:45Z
YoshiRulz
10156
Create test
281646
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36181"
},
"Z2K2": {
"Z1K1": "Z20",
"Z20K1": "Z36174",
"Z20K2": {
"Z1K1": "Z7",
"Z7K1": "Z36174",
"Z36174K1": {
"Z1K1": "Z13518",
"Z13518K1": "7"
}
},
"Z20K3": {
"Z1K1": "Z7",
"Z7K1": "Z36175",
"Z36175K2": [
"Z33198",
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16661"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "0"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1023"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20829"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "1116892707587883"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "2540030189836960"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "3"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "3530822107858469"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "4278419646001971"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "3611886901151138"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "2"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "3314649325744685"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "3611886901151138"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "2"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "3314649325744685"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "3"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "3530822107858469"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "4278419646001971"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
},
{
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "1116892707587883"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z33198K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "2540030189836960"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
}
],
"Z36175K3": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "7"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "1261007895663739"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "7th roots of unity"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
hp2tyu0txedxhndoodc3xi6q9ftdsyg
Z36182
0
85290
281648
2026-06-08T00:04:30Z
YoshiRulz
10156
Create implementation
281648
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36182"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z36174",
"Z14K2": {
"Z1K1": "Z7",
"Z7K1": "Z873",
"Z873K1": "Z35314",
"Z873K2": {
"Z1K1": "Z7",
"Z7K1": "Z13436",
"Z13436K1": "Z35072",
"Z13436K2": {
"Z1K1": "Z33198",
"Z33198K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1023"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20829"
}
},
"Z33198K2": "Z20862"
},
"Z13436K3": {
"Z1K1": "Z7",
"Z7K1": "Z27068",
"Z27068K1": {
"Z1K1": "Z7",
"Z7K1": "Z31079",
"Z31079K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1023"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20829"
}
},
"Z31079K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16661"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "0"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z31079K3": {
"Z1K1": "Z7",
"Z7K1": "Z22605",
"Z22605K1": {
"Z1K1": "Z7",
"Z7K1": "Z20936",
"Z20936K1": {
"Z1K1": "Z18",
"Z18K1": "Z36174K1"
}
}
}
},
"Z27068K2": "Z12967",
"Z27068K3": {
"Z1K1": "Z7",
"Z7K1": "Z31758",
"Z31758K1": {
"Z1K1": "Z7",
"Z7K1": "Z31079",
"Z31079K1": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16662"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "1023"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20829"
}
},
"Z31079K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16661"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "0"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
},
"Z31079K3": {
"Z1K1": "Z7",
"Z7K1": "Z22605",
"Z22605K1": {
"Z1K1": "Z7",
"Z7K1": "Z20936",
"Z20936K1": {
"Z1K1": "Z18",
"Z18K1": "Z36174K1"
}
}
}
},
"Z31758K2": {
"Z1K1": "Z20838",
"Z20838K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16660"
},
"Z20838K2": {
"Z1K1": "Z16683",
"Z16683K1": {
"Z1K1": "Z16659",
"Z16659K1": "Z16661"
},
"Z16683K2": {
"Z1K1": "Z13518",
"Z13518K1": "0"
}
},
"Z20838K3": {
"Z1K1": "Z13518",
"Z13518K1": "0"
},
"Z20838K4": {
"Z1K1": "Z20825",
"Z20825K1": "Z20837"
}
}
}
}
}
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "nth roots of unity as complex128s, composition"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
0vwbj37jgfqsynvo7nm4cahfavgupe8
Z36183
0
85291
281649
2026-06-08T00:11:31Z
YoshiRulz
10156
Create implementation
281649
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36183"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z36174",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z600",
"Z16K2": "const exp = th =\u003E ({ real: Math.cos(th), imaginary: Math.sin(th) });\nfunction Z36174( Z36174K1 ) {\n\tZ36174K1 = Number(Z36174K1);\n\treturn Array.from({ length: Z36174K1 }, (_, i) =\u003E exp(i/Z36174K1 * Math.PI));\n}"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "nth roots of unity as complex128s, JS"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
qlz7fkjdy05j442vyo5ed5lnmidmtbe
281652
281649
2026-06-08T00:18:25Z
YoshiRulz
10156
Fix scaling
281652
zobject
text/plain
{
"Z1K1": "Z2",
"Z2K1": {
"Z1K1": "Z6",
"Z6K1": "Z36183"
},
"Z2K2": {
"Z1K1": "Z14",
"Z14K1": "Z36174",
"Z14K3": {
"Z1K1": "Z16",
"Z16K1": "Z600",
"Z16K2": "const exp = th =\u003E ({ real: Math.cos(th), imaginary: Math.sin(th) });\nfunction Z36174( Z36174K1 ) {\n\tZ36174K1 = Number(Z36174K1);\n\tconst scalar = 2 * Math.PI / Z36174K1;\n\treturn Array.from({ length: Z36174K1 }, (_, i) =\u003E exp(i * scalar));\n}"
}
},
"Z2K3": {
"Z1K1": "Z12",
"Z12K1": [
"Z11",
{
"Z1K1": "Z11",
"Z11K1": "Z1002",
"Z11K2": "nth roots of unity as complex128s, JS"
}
]
},
"Z2K4": {
"Z1K1": "Z32",
"Z32K1": [
"Z31"
]
},
"Z2K5": {
"Z1K1": "Z12",
"Z12K1": [
"Z11"
]
}
}
n3v8i9rndkxairlqky0640duojslkpw