Skip to content

Commit 04bf314

Browse files
committed
fix template_object_to_wikitext(), interwikimap_mapper
1 parent d5fb562 commit 04bf314

5 files changed

Lines changed: 160 additions & 36 deletions

File tree

_test suite/test.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4900,8 +4900,10 @@ function test_wiki() {
49004900
assert(["''v''", CeL.wiki.next_meaningful_element(parsed, 2).toString()], 'CeL.wiki.next_meaningful_element() #1-3');
49014901

49024902
assert(['{{t|v1|v2|p1=vp1|p2=vp2}}', CeL.wiki.parse.template_object_to_wikitext('t', { 1: 'v1', 2: 'v2', p1: 'vp1', p2: 'vp2' })], 'template_object_to_wikitext: #1');
4903-
assert(['{{t|v1|v2|4=v4|p1=vp1}}', CeL.wiki.parse.template_object_to_wikitext('t', { 1: 'v1', 2: 'v2', 4: 'v4', p1: 'vp1' })], 'template_object_to_wikitext: #2');
4903+
assert(['{{t|v1|v2||v4|p1=vp1}}', CeL.wiki.parse.template_object_to_wikitext('t', { 4: 'v4', 2: 'v2', 1: 'v1', p1: 'vp1' })], 'template_object_to_wikitext: #2');
49044904
assert(['{{t|v1|v2|p1=vp1}}', CeL.wiki.parse.template_object_to_wikitext('t', { 1: 'v1', 2: 'v2', p1: 'vp1', q2: 'vq2' }, function (text_array) { return text_array.filter(function (text, index) { return !/^q/.test(text); }); })], 'template_object_to_wikitext: #3');
4905+
assert(['{{t|v1|v2||v4}}', CeL.wiki.parse.template_object_to_wikitext('t', [, 'v1', 'v2', , 'v4'])], 'template_object_to_wikitext: #4');
4906+
assert(['{{t|v1||v3|v4}}', CeL.wiki.parse.template_object_to_wikitext(['t', 'v1', , 'v3', 'v4'])], 'template_object_to_wikitext: #5');
49054907

49064908
// [[Special:ExpandTemplates]]
49074909
assert(['A_B', CeL.wiki.evaluate_parser_function_token.call(CeL.wiki.parse('{{anchorencode:A_B}}')).toString()], 'wiki.evaluate_parser_function_token.call: {{ANCHORENCODE:}} #1');
@@ -5985,11 +5987,35 @@ function test_wiki() {
59855987

59865988
var wikitext = '[[ja:東京]]';
59875989
var parsed = CeL.wiki.parse(wikitext, CeL.wiki.add_session_to_options(zhwiki));
5988-
assert([false, parsed.is_link], 'zhwiki: .is_link #1-1');
5990+
assert([false, !!parsed.is_link], 'zhwiki: .is_link #1-1');
59895991

59905992
wikitext = '[[:ja:東京]]';
59915993
parsed = CeL.wiki.parse(wikitext, CeL.wiki.add_session_to_options(zhwiki));
5992-
assert([true, parsed.is_link], 'zhwiki: .is_link #2-1');
5994+
assert([true, !!parsed.is_link], 'zhwiki: .is_link #1-2');
5995+
5996+
wikitext = '[[ja:]]';
5997+
parsed = CeL.wiki.parse(wikitext, CeL.wiki.add_session_to_options(zhwiki));
5998+
assert([false, !!parsed.is_link], 'zhwiki: .is_link #1-3');
5999+
6000+
wikitext = '[[:ja:]]';
6001+
parsed = CeL.wiki.parse(wikitext, CeL.wiki.add_session_to_options(zhwiki));
6002+
assert([true, !!parsed.is_link], 'zhwiki: .is_link #1-4');
6003+
6004+
wikitext = '[[ : en :]]';
6005+
parsed = CeL.wiki.parse(wikitext, CeL.wiki.add_session_to_options(zhwiki));
6006+
assert([true, !!parsed.is_link], 'zhwiki: .is_link #1-5');
6007+
6008+
wikitext = '[[ <!----> : en :]]';
6009+
parsed = CeL.wiki.parse(wikitext, CeL.wiki.add_session_to_options(zhwiki));
6010+
assert([true, !!parsed.is_link], 'zhwiki: .is_link #1-6');
6011+
6012+
wikitext = '[[ <!----> en :]]';
6013+
parsed = CeL.wiki.parse(wikitext, CeL.wiki.add_session_to_options(zhwiki));
6014+
assert([false, !!parsed.is_link], 'zhwiki: .is_link #1-7');
6015+
6016+
wikitext = '[[w:ja:東京]]';
6017+
parsed = CeL.wiki.parse(wikitext, CeL.wiki.add_session_to_options(zhwiki));
6018+
assert([true, !!parsed.is_link], 'zhwiki: .is_link #2-1');
59936019

59946020
wikitext = '[[commons:東京]]';
59956021
parsed = CeL.wiki.parse(wikitext, CeL.wiki.add_session_to_options(zhwiki));

application/net/wiki/namespace.js

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -908,18 +908,14 @@ function module_code(library_namespace) {
908908
// console.trace([ interwiki_pattern, interwikimap ]);
909909
if (Array.isArray(interwikimap)) {
910910
matched = language.match(interwiki_pattern);
911-
if (matched && interwikimap.some(function(map) {
912-
if (map.prefix === matched[1]) {
913-
// console.log(map);
914-
// API_URL = map.url;
915-
return matched = map
916-
//
917-
.url.replace(/\/wiki\/\$1/, '/w/api.php')
918-
//
919-
.replace(/\$1/, '');
920-
}
921-
})) {
922-
language = matched;
911+
if (matched && interwikimap.mapper[matched[1]]) {
912+
// console.log(interwikimap.mapper[matched[1]]);
913+
// API_URL = interwikimap.mapper[matched[1]].url;
914+
language = interwikimap.mapper[matched[1]]
915+
//
916+
.url.replace(/\/wiki\/\$1/, '/w/api.php')
917+
//
918+
.replace(/\$1/, '');
923919
}
924920
} else if (language in language_code_to_site_alias) {
925921
// e.g., 'lzh' → 'zh-classical'
@@ -3037,12 +3033,22 @@ function module_code(library_namespace) {
30373033
// session.has_languagevariants &&
30383034
configurations.interwikimap;
30393035
if (interwikimap) {
3036+
interwikimap.mapper = Object.create(null);
30403037
// prefix_pattern
30413038
site_configurations.interwiki_pattern = new RegExp('^\\s*('
3042-
+ interwikimap.map(function(interwiki) {
3043-
return interwiki.prefix;
3044-
}).join('|') + ')(?::(.*))?$', 'i');
3045-
// 不可刪除 configurations.interwikimap: 還會用到。
3039+
//
3040+
+ interwikimap.map(function(interwiki_data) {
3041+
var prefix = interwiki_data.prefix;
3042+
var mapper = interwikimap.mapper;
3043+
if (prefix in mapper) {
3044+
library_namespace.error('adapt_site_configurations: '
3045+
//
3046+
+ '重複設定 interwikimap[' + prefix + ']');
3047+
}
3048+
mapper[prefix] = interwiki_data;
3049+
return prefix;
3050+
}).join('|') + ')(?::(.*))?$', 'i');
3051+
// 不可刪除 configurations.interwikimap: language_to_site_name() 還會用到。
30463052
}
30473053

30483054
var language_codes = configurations.languages;
@@ -3069,8 +3075,11 @@ function module_code(library_namespace) {
30693075
// delete configurations.languagevariants;
30703076
}
30713077

3078+
// PATTERN_interlanguage_startup
3079+
// https://en.wikipedia.org/wiki/Help:Interwiki_linking#Prefix_codes_for_linking_to_Wikimedia_sister_projects
3080+
// [ all title, interwiki prefix code, page title ]
30723081
site_configurations.PATTERN_language_startup = new RegExp('^\\s*('
3073-
+ language_codes.join('|') + ')(?::(.*))?$', 'i');
3082+
+ language_codes.join('|') + ')\\s*(?::(.*))?$', 'i');
30743083

30753084
// --------------------------------------------------------------------
30763085

application/net/wiki/parser.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2543,10 +2543,10 @@ function module_code(library_namespace) {
25432543
&& options.reference_anchor_node
25442544
// e.g., [[Talk:拉丁美洲文學]], [[Talk:瓊瑤]]
25452545
&& (typeof token !== 'object' || token.type in {
2546-
comment : true,
2547-
transclusion : true,
25482546
section_title : true,
2549-
category : true
2547+
category : true,
2548+
transclusion : true,
2549+
comment : true
25502550
})
25512551
// && options.insert_after_reference_anchor
25522552
&& options.reference_anchor_position === 'after') {

application/net/wiki/parser/misc.js

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,28 +114,67 @@ function module_code(library_namespace) {
114114
* @param {Object}template_object
115115
* parameters 形式的 object。<br />
116116
* e.g., { '1': value, '2': value, parameter1 : value1 }
117-
* @param {Object}[post_processor]
117+
* @param {Function}[post_processor]
118118
* post-processor for text_array
119119
*/
120120
function template_object_to_wikitext(template_name, template_object,
121121
post_processor) {
122+
if (!post_processor && typeof template_object === 'function') {
123+
// shift arguments.
124+
post_processor = template_object;
125+
template_object = undefined;
126+
}
127+
128+
if (!template_object) {
129+
if (Array.isArray(template_name)) {
130+
// clone
131+
template_object = template_name.slice();
132+
template_name = template_object[0];
133+
template_object[0] = undefined;
134+
} else {
135+
template_object = Object.create(null);
136+
}
137+
}
138+
139+
if (!template_name) {
140+
throw new Error('template_object_to_wikitext: ' + '未指定模板名稱!');
141+
}
142+
122143
var text_array = [ '{{' + template_name ], index = 1;
123144

124-
if (!template_object)
125-
template_object = Object.create(null);
145+
// @see [[Template:Escape template list]]
146+
function escape_parameter(parameter) {
147+
parameter = String(parameter)
148+
//
149+
.replace(/[{}]/g, function(char) {
150+
// <nowiki>char</nowiki>
151+
return '&#' + char.charCodeAt(0) + ';';
152+
})
153+
//
154+
.replace(/\|/g, '{{!}}')
155+
// .replace(/=/g, '{{=}}')
156+
;
157+
return parameter;
158+
}
126159

127160
// 先置放數字 parameters。
128161
while (true) {
129162
var value = template_object[index];
130163
if (!is_valid_parameters_value(value)) {
164+
if (is_valid_parameters_value(template_object[index + 1])
165+
|| is_valid_parameters_value(template_object[index + 2])) {
166+
// 最多可跳過兩個 parameters,簡化結構。
167+
text_array[index++] = '';
168+
continue;
169+
}
131170
break;
132171
}
133172

134173
if (false && typeof value !== 'string') {
135174
value = typeof value.toString === 'function' ? value.toString()
136175
: String(value);
137176
}
138-
value = String(value);
177+
value = escape_parameter(value);
139178

140179
if (value.includes('='))
141180
value = index + '=' + value;
@@ -149,9 +188,10 @@ function module_code(library_namespace) {
149188
// 已處理過。
150189
continue;
151190
}
191+
152192
var value = template_object[key];
153193
if (is_valid_parameters_value(value)) {
154-
value = String(value);
194+
value = escape_parameter(value);
155195
if (value.includes('\n') && !text_array.at(-1).endsWith('\n')) {
156196
text_array[text_array.length - 1] += '\n';
157197
}
@@ -321,14 +361,16 @@ function module_code(library_namespace) {
321361
if (Array.isArray(parameter_value)) {
322362
if (parameter_value.type !== 'plain')
323363
parameter_value = [ parameter_value ];
364+
// @see PATTERN_invalid_page_name_characters.test() @
365+
// parse_transclusion() @ CeL.application.net.wiki.parser.wikitext
324366
parameter_value = parameter_value.filter(function(token) {
325367
// if (options.omit_numbered_parameters === 'lenient')
326368
// 採用比較寬鬆的標準。
327369
return !(token.type in {
328-
parameter : true,
370+
transclusion : true,
329371
// e.g., {{!}} {{=}}
330372
magic_word_function : true,
331-
transclusion : true,
373+
parameter : true,
332374
comment : true
333375
});
334376
}).join('');

application/net/wiki/parser/wikitext.js

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2515,14 +2515,56 @@ function module_code(library_namespace) {
25152515
= wiki_API
25162516
.normalize_title(category_matched[1], options);
25172517
}
2518+
25182519
} else {
2520+
if (interwikimap_mapper
2521+
//
2522+
&& (matched = parameters.page_title.match(/^(\w+):(.+)$/))) {
2523+
matched[1] = matched[1].trim();
2524+
matched.map = interwikimap_mapper[matched[1]];
2525+
if (matched.map) {
2526+
// https://en.wikipedia.org/wiki/Help:Interwiki_linking#Prefix_codes_for_linking_to_Wikimedia_sister_projects
2527+
// https://www.mediawiki.org/wiki/Manual:$wgExtraInterlanguageLinkPrefixes
2528+
parameters.interwiki_prefix = matched[1];
2529+
parameters.interwiki_name = matched[2];
2530+
parameters.interwiki_url = matched.map.url.replace(
2531+
// TODO: should use `new URI()`
2532+
/\$1/, encodeURIComponent(matched[2].replace(/ /g,
2533+
'_')));
2534+
}
2535+
}
2536+
2537+
matched = page_name.toString().replace(
2538+
/<\!--([\s\S]*)-->/g, '');
2539+
// .is_plain_link
25192540
parameters.is_link = !PATTERN_language_startup
2520-
// || /^\s*:/.test(page_name.toString())
2521-
|| !PATTERN_language_startup.test(page_name.toString());
2541+
// || /^\s*:/.test(matched)
2542+
|| !PATTERN_language_startup.test(matched);
2543+
2544+
if (PATTERN_language_startup) {
2545+
matched = /^\w+: *[^\s:]/
2546+
.test(parameters.interwiki_name)
2547+
// e.g., [[w:zh:title]]
2548+
&& parameters.interwiki_name
2549+
.match(PATTERN_language_startup)
2550+
// e.g., [[:zh:w:title]]
2551+
|| parameters.page_title
2552+
.match(PATTERN_language_startup);
2553+
if (matched && matched[2]) {
2554+
// https://en.wikipedia.org/wiki/Help:Interlanguage_links#Inline_links_(links_in_the_text_of_the_article)
2555+
// https://www.mediawiki.org/wiki/Manual:Interwiki#Interwiki_links_to_other_languages
2556+
parameters.interlanguage_prefix = matched[1];
2557+
parameters.interlanguage_title = matched[2];
2558+
// parameters.is_interlanguage_link = true;
25222559

2523-
// https://www.mediawiki.org/wiki/Manual:Interwiki#Interwiki_links_to_other_languages
2524-
// https://www.mediawiki.org/wiki/Manual:$wgExtraInterlanguageLinkPrefixes
2525-
// parameters.is_interlanguage_link = !parameters.is_link;
2560+
} else {
2561+
// e.g., [[BBC]], [[ja]]
2562+
}
2563+
}
2564+
2565+
// TODO: [[w:en:title]], [[:en:w:title]]:
2566+
// parameters.wiki_family_prefix
2567+
// https://en.wikipedia.org/wiki/Wikipedia:Wikimedia_sister_projects#How_to_link
25262568
}
25272569

25282570
if (false) {
@@ -2632,6 +2674,10 @@ function module_code(library_namespace) {
26322674
&& session.configurations && session.configurations.extensiontag_hash
26332675
|| wiki_extensiontags;
26342676

2677+
var interwikimap_mapper = session && session.latest_site_configurations
2678+
&& session.latest_site_configurations.interwikimap
2679+
&& session.latest_site_configurations.interwikimap.mapper;
2680+
26352681
var PATTERN_file_prefix;
26362682
if (session
26372683
// session === wiki_API?
@@ -2991,7 +3037,7 @@ function module_code(library_namespace) {
29913037
// 處理某些特殊屬性的值。
29923038
if (false && /url$/i.test(key)) {
29933039
try {
2994-
// 有些參數值會迴避"=",此時使用decodeURIComponent可能會更好
3040+
// 有些參數值會迴避 "=",此時用 decodeURIComponent() 可能會更好
29953041
value = decodeURI(value);
29963042
} catch (e) {
29973043
// TODO: handle exception
@@ -3169,6 +3215,7 @@ function module_code(library_namespace) {
31693215

31703216
// 假如 parameter name 有特殊的 token 就不該視為 template。
31713217
// 警告: 這個方法可能將非模板當成模板。
3218+
// @see may_omit_numbered_parameter_value()
31723219
if (PATTERN_invalid_page_name_characters.test((Array
31733220
.isArray(parameters[0]) ? parameters[0]
31743221
: [ parameters[0] ]).filter(function(token) {

0 commit comments

Comments
 (0)