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> &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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". &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 16:27, 12 May 2026 (UTC) == Display function for HTML fragment == Currently, any collapsed Z89 literal appears as<blockquote>&lt;&gt; [[Z89|HTML fragment]]</blockquote>If I were to create a new Function which returned something like<blockquote>&lt;&gt; 123-byte HTML fragment <q><nowiki><td><span lang=</nowiki>&hellip;</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). &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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}}&mdash;[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}}&mdash;[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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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). &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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! &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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? &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 21:09, 24 April 2026 (UTC) :I've created {{Z|Z34122}} as an extension to {{Z|Z34039}} for larger functions. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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... &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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&trade;'' 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 (&#x300C;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!&#x300D;). &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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. &mdash; [[User:Theki|rae<sup>5e</sup>]] &lt;[[User talk:Theki|talk]]&gt; 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;" | (&hellip;) ! style="text-align: start;" | &rarr; {{{1}}} | [[Special:Search/: "Z8K2 {{{1}}}"|Search for <code>{{nowrap|"Z8K2 {{{1}}}"}}</code> in mainspace]] |- ! style="text-align: end;" | {{nowrap|({{{1}}}, &hellip;)}} ! style="text-align: start;" | &rarr; &hellip; | [[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|(&not;{{{1}}}, &hellip;, {{{1}}}, &hellip;)}} ! style="text-align: start;" | &rarr; &hellip; | [[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|(&hellip;, {{{1}}}, &hellip;)}} ! style="text-align: start;" | &rarr; {{{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;" | (&hellip;) ! style="text-align: start;" | {{nowrap|&rarr; &lt;{{{1}}}&gt;[&hellip;]}} | [[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|(&hellip;, &lt;{{{1}}}&gt;[&hellip;], &hellip;)}} ! style="text-align: start;" | &rarr; &hellip; | [[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|(&hellip;, &lt;{{{1}}}&gt;[&hellip;], &hellip;)}} ! style="text-align: start;" | {{nowrap|&rarr; &lt;{{{1}}}&gt;[&hellip;]}} | [[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}}}.&#32;}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;|}}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}}}.&#32;&#32;}}{{#if:{{{2|}}}|For {{{2}}}, see {{#if:{{{3|}}}|[[{{{3}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.&#32;&#32;|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)]]}}.&#32;&#32;}}}}{{#if:{{{4|}}}|{{#if:{{{6|}}}|{{#ifeq:{{{6}}}|1|For other uses, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{6}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.&#32;&#32;}}}}{{#if:{{{6|}}}|{{#if:{{{8|}}}|{{#ifeq:{{{8}}}|1|For other uses, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{8}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.&#32;&#32;}}}}}}}}}}}}<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}}}.&#32;&#32;}}{{#if:{{{2|}}}|For {{{2}}}, see {{#if:{{{3|}}}|[[{{{3}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.&#32;&#32;|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)]]}}.&#32;&#32;}}}}{{#if:{{{4|}}}|{{#if:{{{6|}}}|{{#ifeq:{{{6}}}|1|For other uses, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{6}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.&#32;&#32;}}}}{{#if:{{{6|}}}|{{#if:{{{8|}}}|{{#ifeq:{{{8}}}|1|For other uses, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{8}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.&#32;&#32;}}}}}}}}}}}}<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}}}.&#32;&#32;}}{{#if:{{{2|}}}|For {{{2}}}, see {{#if:{{{3|}}}|[[{{{3}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.&#32;&#32;|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)]]}}.&#32;&#32;}}}}{{#if:{{{4|}}}|{{#if:{{{6|}}}|{{#ifeq:{{{6}}}|1|For other uses, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{6}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.&#32;&#32;}}}}{{#if:{{{6|}}}|{{#if:{{{8|}}}|{{#ifeq:{{{8}}}|1|For other uses, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.|For {{{8}}}, see {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}.&#32;&#32;}}}}}}}}}}}}<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}}}.&#32;&#32; }}{{#if:{{{2|}}} |For {{{2}}}, see {{#if:{{{3|}}} |[[{{{3}}}]] |[[{{PAGENAME}} (disambiguation)]] }}.&#32;&#32; |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)]] }}.&#32;&#32; }} }}{{#if:{{{4|}}} |{{#if:{{{6|}}} |{{#ifeq:{{{6}}}|1 |For other uses, see {{#if:{{{7|}}} |[[{{{7}}}]] |[[{{PAGENAME}} (disambiguation)]] }}. |For {{{6}}}, see {{#if:{{{7|}}} |[[{{{7}}}]] |[[{{PAGENAME}} (disambiguation)]] }}.&#32;&#32; }} }}{{#if:{{{6|}}} |{{#if:{{{8|}}} |{{#ifeq:{{{8}}}|1 |For other uses, see {{#if:{{{9|}}} |[[{{{9}}}]] |[[{{PAGENAME}} (disambiguation)]] }}. |For {{{8}}}, see {{#if:{{{9|}}} |[[{{{9}}}]] |[[{{PAGENAME}} (disambiguation)]] }}.&#32;&#32; }} }} }} }} }} }}<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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|{{(D)|{{{3}}}}}{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|{{(D)|{{{5}}}}}{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|{{(D)|{{{7}}}}}|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|{{(D)|{{{7}}}}}{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|{{(D)|{{{9}}}}}|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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}}}.&#32;&#32;}}<!-- -->For {{#if:{{{2|}}}|{{{2}}}|other uses}}, see {{#if:{{{3|}}}|[[{{{3}}}]]{{#ifeq:{{{4|}}}|and|&#32;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, ..." -->&#32;&#32;For {{#ifeq:{{{4}}}|1|other uses|{{{4}}}}}, see {{#if:{{{5|}}}|[[{{{5}}}]]{{#ifeq:{{{6|}}}|and|&#32;and {{#if:{{{7|}}}|[[{{{7}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{6|}}}|<!-- -->{{#ifeq:{{{6|}}}|and||<!-- -->&#32;&#32;For {{#ifeq:{{{6}}}|1|other uses|{{{6}}}}}, see {{#if:{{{7|}}}|[[{{{7}}}]]{{#ifeq:{{{8|}}}|and|&#32;and {{#if:{{{9|}}}|[[{{{9}}}]]|[[{{PAGENAME}} (disambiguation)]]}}}}|[[{{PAGENAME}} (disambiguation)]]}}.}}<!-- -->{{#if:{{{8|}}}|<!-- -->{{#ifeq:{{{8|}}}|and||<!-- -->&#32;&#32;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 .. ' §&nbsp;' .. 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 .. ' §&nbsp;' .. 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 §&nbsp;%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 .. ' §&nbsp;' .. 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 §&nbsp;%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 §&nbsp;%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('§&nbsp;%s', section) elseif section then display = string.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = string.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = string.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = string.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = string.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = string.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = string.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = string.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) elseif section then display = mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) else return mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) else return mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) else return mw.ustring.format('%s §&nbsp;%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('§&nbsp;%s', section) else return mw.ustring.format('%s §&nbsp;%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