cejs
Version:
A JavaScript module framework that is simple to use.
1,548 lines (1,342 loc) • 48.9 kB
JavaScript
/**
* @name CeL function for MediaWiki (Wikipedia / 維基百科):
* 常用模板特設功能。本工具檔放置的是幾乎所有wiki計畫通用的模板,或者少數wiki計畫特有、且大量使用的著名模板。對各wiki有不同用途的模板,應放置於個別namespace下。
*
* 注意: 本程式庫必須應各 wiki project 模板內容改動而改寫。
*
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
*
* TODO:<code>
Read https://www.mediawiki.org/wiki/Extension:TemplateData https://www.mediawiki.org/wiki/Help:TemplateData
合併 `special page configuration.js`
</code>
*
* @since 2019/12/4 7:16:34
*/
// More examples: see /_test suite/test.js
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
;
// 'use asm';
// @examples
(function() {
CeL.run([ 'application.net.wiki',
//
'application.net.wiki.template_functions' ]);
// will auto-load functions @ template_functions/site_name.js
// e.g., template_functions/zhwiki.js
var wiki = new CeL.wiki({});
wiki.page('title').parse(function for_parsed(parsed) {
// var page_data = parsed.page;
parsed.each('template:Al', function(token) {
// ...
}, {
// auto-loading functions @ template_functions
bind_template_functions : true
});
});
// alternative method:
wiki.page('title', function for_page(page_data) {
/** {Array} parsed page content 頁面解析後的結構。 */
var parsed = wiki.parse(page_data);
parsed.each('template:Al', function(token) {
// ...
}, {
// [KEY_SESSION]
session : wiki,
// auto-loading functions @ template_functions
bind_template_functions : true
});
});
});
// --------------------------------------------------------------------------------------------
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
typeof CeL === 'function' && CeL.run({
// module name
name : 'application.net.wiki.template_functions',
require : 'data.native.' + '|application.net.wiki.'
// load MediaWiki module basic functions
+ '|application.net.wiki.namespace.'
//
+ '|application.net.wiki.parser.',
// 設定不匯出的子函式。
no_extend : '*',
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
code : module_code
});
function module_code(library_namespace) {
// requiring
var wiki_API = library_namespace.application.net.wiki, KEY_SESSION = wiki_API.KEY_SESSION;
// @inner
// var is_api_and_title = wiki_API.is_api_and_title,
// normalize_title_parameter = wiki_API.normalize_title_parameter;
var to_exit = wiki_API.parser.parser_prototype.each.exit;
// --------------------------------------------------------------------------------------------
function template_functions() {
}
function get_parsed(page_data) {
if (!page_data)
return;
var parsed = typeof page_data.each === 'function'
// `page_data` is parsed data
? page_data : wiki_API.parser(page_data);
return parsed;
}
// --------------------------------------------------------------------------------------------
// usage:
if (false) {
// ...
var parsed = page_data.parse();
if (parsed.is_biography()) {
// ...
}
}
template_functions.biographical_templates = [
//
'birth date', 'birth date and age', 'birth year and age',
//
'death date', 'death date and age', 'death year and age'
//
].map(function(template_name) {
return wiki_API.normalize_title('Template:' + template_name);
});
template_functions.biographical_categories = [ 'Living people',
'Year of birth missing', 'Year of death missing' ];
/**
* Test if `page_data` is biography of a person. 頁面為人物傳記。
*
* @param {Object}page_data
* page data
*
* @returns {Boolean} `page_data` is biography of a person.
*
* @see {{WikiProject Biography}}
*/
function page_is_biography() {
// var page_data = this;
// console.log(page_data);
// var parsed = CeL.wiki.parser(page_data).parse();
var parsed = this;
var session = wiki_API.session_of_options(parsed)
// needed in 20210422.Sorting_category_and_sort_key_of_Thai_names.js
|| wiki_API.session_of_options(parsed.options);
var is_biography;
parsed.each('template', function(token) {
if (session.is_template(template_functions.biographical_templates,
token)) {
is_biography = true;
return parsed.each.exit;
}
});
if (is_biography)
return true;
parsed.each('category', function(token) {
if (template_functions.biographical_categories.includes(token.name)
// e.g., en: [[Category:2000 births]] [[Category:2000 deaths]]
// zh: [[Category:2000年出生]] [[Category:2000年逝世]]
// ja: [[Category:2000年生]] [[Category:2000年没]]
|| /^\d+(?: births| deaths|年出生|年逝世|年生|年没)$/.test(token.name)) {
is_biography = true;
return parsed.each.exit;
}
});
if (is_biography)
return true;
library_namespace.debug(wiki_API.title_link_of(parsed.page)
+ ' is ot biography?', 3, 'page_is_biography');
}
// ------------------------------------------------------------------------
// template names: The first one is the main template name. 首個名稱為正式名稱。
var Hat_names = 'TalkendH|Talkendh|Delh|Closereq|Hat|Hidden archive top'
.split('|').to_hash();
var Multidel_names = 'Multidel'.split('|').to_hash();
var Old_vfd_multi__names = 'Old vfd multi|Oldafdfull|Vfd-kept|存廢討論被保留|頁面存廢討論被保留'
.split('|');
var Old_vfd_multi__main_name = Old_vfd_multi__names[0];
Old_vfd_multi__names = Old_vfd_multi__names.to_hash();
// var Old_vfd_multi__main_name = Object.keys(Old_vfd_multi__names)[0];
var Article_history__names = 'Article history|ArticleHistory|Article milestones|AH'
.split('|');
var Article_history__main_name = Article_history__names[0];
Article_history__names = Article_history__names.to_hash();
// ------------------------------------------------------------------------
// const
// var NS_MediaWiki = wiki_API.namespace('MediaWiki');
var NS_Module = wiki_API.namespace('Module');
var NS_Template = wiki_API.namespace('Template');
var PATTERN_Item_function = /(\W)Item\s*\(arg,arg(?:,arg)?\)/g;
PATTERN_Item_function = new RegExp(PATTERN_Item_function.source.replace(
/arg/g, /\s*(nil|"(?:\\"|[^"]+)*"|'(?:\\'|[^']+)*')\s*/.source),
'g');
function item_to_conversion(item) {
// assert: item = {type: 'item', rule: '', original: ''}
var conversion = wiki_API.parse('-{A|' + item.rule + '}-', {
normalize : true,
with_properties : true
});
conversion.item = item;
return conversion;
}
// 汲取 page_data 中所有全局轉換(全頁面語言轉換),並交給 processor 處理。
// processor({type: 'item', rule: '', original: ''})
// Warning: Will modify `page_data.parsed`
// @see routine/20191129.check_language_conversion.js
function parse_conversions(page_data, options) {
var conversion_list = [];
if (!page_data)
return conversion_list;
if (page_data.ns === NS_Module) {
var matched = page_data.title
// [[w:zh:Wikipedia:字詞轉換處理/公共轉換組#Lua版建立]]
// core: [[Module:CGroup/core]]
.match(/\/(list|doc|temp|sandbox|preload|editintro|core)$/i);
if (matched) {
library_namespace.info('parse_conversions: '
// document / temporary / list
+ 'Skip special page: ' + wiki_API.title_link_of(page_data));
conversion_list.skipped = matched[1];
return conversion_list;
}
if (false) {
library_namespace.info('parse_conversions: ' + page_data.title);
}
var object = wiki_API.content_of(page_data, options);
// local function Item
if (/function\s+Item\s*\(/.test(object)
// `local Item = require('Module:CGroup/core').Item;`
|| /local\s+Item\s*=/.test(object)) {
// remove local function Item(o, r)
object = object.replace(
/(?:\w* )*function Item\s*\([\s\S]+?[\n ]end *\n/, '')
.replace(/local\s+Item\s*=[^\n;]+/, '');
// convert `Item('Alec', 'zh-cn:亚历克; zh-tw:亞歷;')`
object = object.replace(PATTERN_Item_function, function(item,
prefix, original, rule, additional) {
if (additional) {
library_namespace.warn(
// 含有未知參數 [[:Category:Unknown parameters]]
'There is additional parameter: ' + item.trim());
}
return prefix + "{type='item',original=" + original
+ ",rule=" + rule + "}";
});
} else if ((matched = object
// e.g., [[Module:CGroup/C]], [[Module:CGroup/HalfLife]]
.match(/^(?:--.*\n)*[\s\n]*return\s+(require)\s*\(\s*(['"])([^\n]+?)\2\s*\)\s*;?/))
|| (matched = object.match(
// e.g., [[Module:CGroup/mw]], [[Module:CGroup/資訊科技]]
/^(?:--.*\n)*[\s\n]*return\s+(require)\s*(\[\[)\s*([^\n]+?)\s*\]\]\s*;?/
//
)) || (matched = object.match(
// e.g., [[Module:CGroup/Mythology]]
/^(?:--.*\n)*[\s\n]*return\s+{\s*(name)\s*=\s*(['"])([^\n]+?)\2\s*}\s*;?/
//
))) {
matched = (matched[1] === 'name' ? 'Module:CGroup/' : '')
+ matched[3];
library_namespace.info('parse_conversions: 公共轉換組模塊 '
+ wiki_API.title_link_of(page_data)
// 重定向
+ ' 重新導向至: ' + wiki_API.title_link_of(matched));
conversion_list.skipped = 'redirected';
conversion_list.redirect_to = matched;
return conversion_list;
}
object = wiki_API.parse.lua_object(object);
object = object && object.content;
if (!Array.isArray(object)) {
library_namespace
.error('parse_conversions: Invalid conversion group: '
+ wiki_API.title_link_of(page_data));
conversion_list.error = 'invalid';
return conversion_list;
}
// console.log(object);
conversion_list = object.filter(function(item) {
return item && item.rule;
}).map(item_to_conversion);
return conversion_list;
}
// ----------------------------------------------------------
// console.log(page_data);
var parsed = wiki_API.content_of(page_data), redirect_to = wiki_API.parse
.redirect(parsed);
if (redirect_to) {
library_namespace.info('parse_conversions: 公共轉換組模塊 '
+ wiki_API.title_link_of(page_data)
// 重定向
+ ' 重新導向至: ' + wiki_API.title_link_of(redirect_to));
conversion_list.skipped = 'redirected';
conversion_list.redirect_to = redirect_to;
return conversion_list;
}
if (page_data.title
&& page_data.title.startsWith('MediaWiki:Conversiontable/')) {
// assert: page_data.ns === NS_MediaWiki
parsed = wiki_API.parser(parsed.replace(
// @see PATTERN_language_conversion @
// CeL.application.net.wiki.parser.wikitext
// MediaWiki:Conversiontable/* 可接受 "\n"
/-{([\s\S]+?)}-/g, function(all, inner) {
return '-{H|' + inner
// trim any trailling comments starting with '//'
// `//blah blah`是可省的注釋,其目的是解釋該轉換規則;
.replace(/\/\/[^\n]+/g, function(all) {
// 如果使用//作注釋的話,;要放在注釋的後面。
var matched = all.match(/;\s*$/);
return matched ? matched[0] : '';
})
// 每條轉換須用如下格式書寫:* abc => xyz //blah blah ;
.replace(/["'*#\n]/g, '').replace(/=>/g, '=>'
// language code
+ page_data.title.match(/\/([^\/]+)/)[1] + ':') + '}-';
}));
} else {
// e.g., page_data.ns === NS_Template
parsed = get_parsed(page_data);
}
parsed.each('convert', function(token, index, parent) {
if (parent.type === 'convert' || !(token.flag in {
// add rule for convert code (but no display in placed code)
H : true,
// add rule for convert code (all text convert)
A : true
})) {
return;
}
// get conversion rule only
var rule = token.toString('rule');
if (rule) {
conversion_list.push(item_to_conversion({
type : 'item',
rule : rule
}));
}
});
function for_each_template_token(token) {
if (token.conversion_list) {
// assert: is {{NoteTA}}
// conversion_list.push({item:item});
return;
}
// console.log(token);
var item = {
type : 'item'
};
switch (token.name) {
case 'CItemLan/R':
// {{CItemLan/R|原文|轉換語法}}
item.rule = token.parameters[2];
if (token.parameters[1])
item.original = token.parameters[1];
break;
case 'CItemLan':
// {{CItemLan|轉換語法|原文}}
item.rule = token.parameters[1];
if (token.parameters[2])
item.original = token.parameters[2];
break;
case 'CItemHidden':
case 'CI':
case 'CItem':
case 'CNoteA':
// {{CItemHidden|轉換規則|original=原文}}
if (token.parameters.display) {
if (String(token.parameters.display).toLowerCase() === 'yes') {
return;
}
item.display = token.parameters.display;
}
item.rule = token.parameters[1];
if (token.parameters.original)
item.original = token.parameters.original;
break;
}
if (!item.rule) {
var matched = token.name.match(/^CGroup\/([^\/]+)/);
if (matched) {
if (!conversion_list.transclusions)
conversion_list.transclusions = [];
conversion_list.transclusions.push(matched[1]);
}
return;
}
if (Array.isArray(item.rule)) {
item.rule.forEach(function(token, index) {
/**
* e.g.,<code>
parsed = CeL.wiki.parse("{{CItem|宿命之子誕生{{=}}>zh-cn:新生|desc=|original=The Hatchling}}");
</code>
*/
if (token.is_magic_word && token.name === '=')
item.rule[index] = '=';
});
}
conversion_list.push(item_to_conversion(item));
}
parsed.each('template', for_each_template_token);
if (conversion_list.transclusions) {
library_namespace.info('parse_conversions: '
//
+ (page_data.ns === NS_Module
//
|| page_data.ns === NS_Template ? '公共轉換組模塊' : '頁面 ')
+ wiki_API.title_link_of(page_data) + ' 內嵌轉換組: '
+ conversion_list.transclusions.join(', '));
conversion_list.categories = parsed.get_categories();
}
return conversion_list;
}
// ------------------------------------------
// https://zh.wikipedia.org/wiki/Template:TalkendH
// [0]: 正式名稱。
var result_flags__Hat = {
// 快捷碼:關閉存廢討論
// 請求無效
rep : '重複提出,無效',
commons : '應在維基共享資源提請',
ne : 'notexist|目標頁面或檔案不存在',
nq : 'notqualified|提刪者未取得提刪資格',
// 保留
dan : '刪後重建',
// 刪除
ic : '圖像因侵權被刪|ifd',
// 快速刪除
lssd : '無來源或版權資訊,快速刪除',
svg : '已改用SVG圖形,快速刪除',
nowcommons : '維基共享資源已提供,快速刪除',
drep : '多次被刪除,條目被白紙保護,禁止創建',
// 轉移至其他維基計畫
twc : '轉移至維基共享資源',
tws : '轉移至維基文庫',
twb : '轉移至維基教科書',
twq : '轉移至維基語錄',
twt : '轉移至維基詞典',
twvoy : '轉移至維基導遊',
two : '轉移至其他維基計畫',
// 其他處理方法
c : '轉交侵權|copyvio',
// m2pfd : '轉送頁面存廢討論',
// m2ifd : '轉送檔案存廢討論',
cr : '分類重定向',
ma : '允許併入|mergeapproved',
// 快捷碼:破壞
del : '已刪除|相關頁面已刪除'
};
// https://zh.wikipedia.org/wiki/Template:Old_vfd_multi
// [0]: 正式名稱。
var result_flags__Old_vfd_multi = {
k : 'keep|kept|保留',
nc : 'no consensus|nc|無共識|无共识',
m : 'moved|move|renamed|移動|移动',
r : 'redirect|redirected|重定向',
// incubated : 'incubated',
// 包含提刪者撤回
sk : 'speedy keep|speedily kept|快速保留|速留',
ir : 'invalid request|無效|无效|請求無效',
// '''請求理由已消失''',页面'''保留''' reason disappeared
rr : '請求理由消失',
merge : 'merge|merged|併入|合併',
d : 'delete|deleted|snowd|刪除',
sd : 'speedy delete|speedily deleted|快速刪除',
tk : 'temporarily keep|暫時保留|暂时保留'
};
// default flag: k|保留
var default_result_of__Old_vfd_multi = 'k';
var result_flags__Article_history = {
renamed : 'rename|renamed'
};
function normalize_result_flag_sets(flag_sets) {
for ( var flag in flag_sets)
flag_sets[flag] = flag_sets[flag].split('|');
// return flag_sets;
}
normalize_result_flag_sets(result_flags__Old_vfd_multi);
normalize_result_flag_sets(result_flags__Hat);
function normalize_result_flag(flag_sets, result, valid_flag_only) {
result = result && result.toString().trim();
var normalized_result = result && result.toLowerCase();
if (normalized_result in flag_sets)
return normalized_result;
for ( var flag in flag_sets) {
var flags = flag_sets[flag];
if (flags.includes(normalized_result)) {
return flag;
// result = flag;
// break;
}
}
if (!valid_flag_only)
return result;
if (false) {
library_namespace.warn('normalize_result_flag: Invalid result: '
+ result);
}
}
function parse_Hat(token, options) {
options = library_namespace.setup_options(options);
if (!token)
return;
var flags;
if (token.type === 'section') {
// console.log(token);
token.each('template', function(_token) {
// console.log(_token);
var _flags = parse_Hat(_token);
// console.log(_flags);
if (_flags) {
flags = _flags;
// 僅以第一個有結論的為主。 e.g., [[Wikipedia:頁面存廢討論/記錄/2010/09/26#158]]
return flags.result && token.each.exit;
}
});
return flags;
}
if (!(token.name in Hat_names))
return;
flags = Object.create(null);
// {{Talkendh|result 處理結果|target}}
// 早期{{delh}}沒有 result 處理結果。
if (token.parameters[1] !== undefined) {
flags.result = token.parameters[1];
}
if (token.parameters[2] !== undefined) {
flags.target = token.parameters[2];
}
// console.log(token);
// console.log(flags);
return flags;
}
function text_of_Hat_flag(flag, allow__Old_vfd_multi__flags) {
if (flag && flag.result) {
// is item
flag = flag.result;
}
var result = normalize_result_flag(result_flags__Old_vfd_multi, flag,
true);
if (result) {
return allow__Old_vfd_multi__flags ? result
: result_flags__Old_vfd_multi[result][0];
}
result = normalize_result_flag(result_flags__Hat, flag, true);
if (result) {
return result_flags__Hat[result][0];
}
library_namespace.debug('Not normal Hat flag: ' + flag, 1,
'text_of_Hat_flag');
return flag;
}
// test if flag includes new_flag:
// CeL.wiki.template_functions.Hat.result_includes('快速刪除', 'd') === true
// '快速刪除' → 'speedy delete'
// 'd' → 'delete'
// 'speedy delete'.includes('delete');
function Hat_flag_result_includes(flag, new_flag) {
// .toLowerCase(): e.g., [[Talk:以色列]]
flag = text_of_Hat_flag(flag, true);
if (!flag)
return;
flag = String(flag).trim().toLowerCase();
new_flag = text_of_Hat_flag(new_flag, true);
if (!new_flag)
return;
new_flag = String(new_flag).trim().toLowerCase();
return flag.includes(new_flag);
}
function parse_Old_vfd_multi_page(page_data, options) {
options = library_namespace.setup_options(options);
var item_list = [], Article_history_items = [], parsed = get_parsed(page_data);
var check_parameters = 'date|result|page|target'.split('|');
if (Array.isArray(options.additional_parameters)) {
// 讀取 .hat_result , .bot_checked 之類。
check_parameters.append(options.additional_parameters);
}
parsed.each('template', function(token) {
var _item_list = parse_Article_history_token(token);
if (_item_list) {
_item_list.forEach(function(item) {
Article_history_items.push(item);
var action = item.action
// allow `action=afd`
&& item.action.toString().toUpperCase();
if (action !== 'AFD' && action !== 'CSD')
return;
item.date = item.date.to_Date().format('%Y/%2m/%2d');
// item.page = item.link;
item_list.push(item);
});
return;
}
// TODO: {{Drv-kept}}
if (!(token.name in Old_vfd_multi__names))
return;
// console.log(token.toString());
// {{Old vfd multi|提刪日期|處理結果|page=頁面名稱}}
var result = token.parameters[2];
result = normalize_result_flag(result_flags__Old_vfd_multi, result)
|| default_result_of__Old_vfd_multi;
var item = {
// 注意: 其他 parameters 會被捨棄掉!
date : token.parameters[1],
result : result,
page : token.parameters.page,
// move to, merge to, redirects to
target : token.parameters[3]
};
if (Array.isArray(options.additional_parameters)) {
options.additional_parameters.forEach(function(parameter) {
if (parameter in token.parameters)
item[parameter] = token.parameters[parameter];
});
}
item = wiki_API.parse.set_template_object_parameters(
//
null, item);
item_list.push(item);
// if (token.parameters.multi) item = [ item ];
for (var index = 2; index < 9
//
&& token.parameters['date' + index]; index++) {
item = Object.create(null);
check_parameters.forEach(function(parameter) {
var name = parameter + index;
if (name in token.parameters)
item[parameter] = token.parameters[name];
});
if (!item.result) {
item.result = default_result_of__Old_vfd_multi;
}
item = wiki_API.parse
.set_template_object_parameters(null, item);
item_list.push(item);
if (index > 5) {
library_namespace.warn('parse_Old_vfd_multi: '
+ wiki_API.title_link_of(page_data)
+ ' Invalid NO ' + index);
}
}
// return to_exit;
});
// if (page_data.title.includes('')) console.log(item_list);
if (options.unique) {
// remove duplicate records
item_list = Old_vfd_multi__unique_item_list(item_list);
}
if (options.using_data) {
item_list.page_data = page_data;
} else if (page_data.title) {
// normalized page title
item_list.page_title = page_data.title;
}
if (Article_history_items.length > 0)
item_list.Article_history_items = Article_history_items;
return item_list;
}
function Old_vfd_multi__unique_item_list(item_list) {
var key_hash = Object.create(null);
function key_filter(item) {
if (!item)
return;
var key = [ item.date && CeL.Julian_day(item.date.to_Date()),
text_of_Hat_flag(item.result, true),
wiki_API.title_of(item.page), item.target ].join('|');
// if (item.date === '') console.log(key);
if (!(key in key_hash)) {
key_hash[key] = item;
return true;
}
}
return item_list.filter(key_filter);
}
function Old_vfd_multi__item_list_to_template_object(item_list, options,
page_data) {
options = library_namespace.setup_options(options);
var force_set_page = 'force_set_page' in options ? options.force_set_page
// 為了預防頁面被移動時出現問題,預設強制加上 `page` 設定。
: true;
var additional_parameters = options.additional_parameters;
if (!Array.isArray(additional_parameters)) {
if (additional_parameters) {
library_namespace
.error('Old_vfd_multi__item_list_to_template_object: Invalid additional_parameters: '
+ additional_parameters);
}
additional_parameters = [];
}
var template_object = Object.create(null);
if (item_list.length > 5) {
library_namespace
.warn('Old_vfd_multi__item_list_to_template_object: {{'
+ Old_vfd_multi__main_name
+ '}} only support 5 records!');
console.log(item_list);
}
var page_title = wiki_API.title_of(page_data);
var set_parameters = wiki_API.parse.set_template_object_parameters;
item_list.forEach(function(item, index) {
if (index === 0) {
var parameters = {
'1' : item.date,
'2' : item.result,
page : (force_set_page || item.page !== page_title)
&& item.page,
// move to, merge to, redirects to
'3' : item.target
};
additional_parameters.forEach(function(parameter_name) {
parameters[parameter_name] = item[parameter_name];
});
set_parameters(template_object, parameters);
return;
}
if (++index === 2) {
template_object.multi = 1;
}
var mapping = {
date : 'date' + index,
result : 'result' + index,
page : 'page' + index,
target : 'target' + index
};
additional_parameters.forEach(function(parameter_name) {
mapping[parameter_name] = parameter_name + index;
});
mapping = Object.reverse_key_value(mapping);
if (!force_set_page && item.page === page_title) {
delete mapping.page;
}
set_parameters(template_object, mapping, item);
});
return template_object;
}
function Old_vfd_multi__item_list_to_wikitext(item_list, options, page_data) {
// console.log(item_list);
var wikitext = Array.isArray(item_list) ?
// normalize wikitext
Old_vfd_multi__item_list_to_template_object(item_list, options,
page_data) : item_list;
/**
* <code>
+ new line
{{Old vfd multi|2008/11/22|k|page=124}}
->
{{Old vfd multi|2008/11/22|k|page=124
|multi=1
|...
}}
</code>
*/
var line_separator = '\n';
var latest_index;
function add_line_separator(string_list) {
var multi;
string_list.forEach(function(parameter, index) {
if (index === 0)
return;
if (/^multi=/.test(parameter)) {
multi = true;
string_list[index - 1] += line_separator;
// string_list[index] += line_separator;
return;
}
var matched = parameter.match(/^[^=\d]+(\d+)=/);
if (matched && latest_index !== +matched[1]) {
latest_index = +matched[1];
string_list[index - 1] += line_separator;
}
});
if (multi) {
// 將 '}}' 前一個加上 line_separator。
string_list[string_list.length - 1] += line_separator;
}
return string_list;
}
if (typeof wikitext === 'object') {
wikitext = wiki_API.parse.template_object_to_wikitext(
Old_vfd_multi__main_name, wikitext, add_line_separator);
}
return wikitext;
}
/**
* 將 page_data 中的 {{Old vfd multi}} 替換成 replace_to。
*
* @param {Object|String}page_data
* @param {Array|Object|String}replace_to
* @param {Object}[options]
* 附加參數/設定選擇性/特殊功能與選項
*/
function replace_Old_vfd_multi(page_data, replace_to, options) {
replace_to = Old_vfd_multi__item_list_to_wikitext(replace_to, options,
page_data);
if (typeof replace_to !== 'string' || !replace_to.startsWith('{{')) {
throw new Error('replace_Old_vfd_multi: Invalid replace_to: '
+ replace_to);
}
var replaced;
var parsed = get_parsed(page_data);
parsed.each('template', function(token) {
if (token.name in Old_vfd_multi__names) {
if (replaced)
return parsed.each.remove_token;
replaced = true;
return replace_to;
}
var item_list = parse_Article_history_token(token);
if (item_list) {
var NO_to_delete = [], last_need_preserve;
item_list.forEach(function(item, index) {
++index;
var action = item.action
// allow `action=afd`
&& item.action.toString().toUpperCase();
if (action === 'AFD')
NO_to_delete.push(index);
else
last_need_preserve = index;
});
if (!last_need_preserve) {
// Only action=AFD
return parsed.each.remove_token;
}
if (NO_to_delete.length === 0) {
if (!replaced) {
library_namespace.warn('replace_Old_vfd_multi: '
+ 'Should find {{' + Article_history__main_name
+ '}} action=AFD, but no {{'
+ Article_history__main_name
+ '}} action=AFD found:');
console.warn(token);
}
return;
}
if (last_need_preserve > NO_to_delete[0]) {
if (typeof options.modify_Article_history_warning) {
options.modify_Article_history_warning(
//
token, page_data);
} else {
library_namespace.warn('replace_Old_vfd_multi: '
//
+ 'Should modify {{' + Article_history__main_name
+ '}} manually:');
library_namespace.log(token.toString());
}
return;
}
// assert: last_need_preserve < NO_to_delete[0]
var PATTERN = new RegExp('^\\s*action(?:'
+ NO_to_delete.join('|') + ')'), index = 1;
while (index < token.length) {
if (PATTERN.test(token[index]))
token.splice(index, 1);
else
index++;
}
// assert: token.length > 1
if (false && token.length === 1) {
// Nothing left
return parsed.each.remove_token;
}
}
if (token.name in Multidel_names) {
library_namespace.warn('replace_Old_vfd_multi: '
//
+ 'Should modify {{Multidel}} manually:');
library_namespace.log(token.toString());
return;
}
}, true);
if (!replaced) {
var line_separator = '\n';
// TODO: 將模板放在專題模板之後。
if (!replace_to.endsWith(line_separator)) {
// 前面的 replaced 是替代,無需加入換行。
// 這邊將會把 `replace_to` 直接添加在頁面頂端,因此需要格式化一下。
parsed.unshift(line_separator);
}
parsed.unshift(replace_to);
}
return parsed.toString();
}
// @see
// https://en.wikipedia.org/wiki/Wikipedia:Wikipedia_Signpost/2008-03-24/Dispatches
function parse_Article_history_token(token, item_list) {
if (!(token.name in Article_history__names))
return;
if (!item_list)
item_list = [];
// TODO: dykdate, itndate [[Module:Article history/config]]
for ( var key in token.parameters) {
var value = token.parameters[key];
var matched = key.match(/^action([1-9]\d?)(.*)?$/);
if (!matched) {
if (library_namespace.is_digits(key)) {
// invalid numeral parameters
// e.g., [[Talk:香港國際機場]]
library_namespace.debug('Skip [' + key + ']: ' + value, 3,
'parse_Article_history_token');
} else {
item_list[key] = value;
}
continue;
}
var index = matched[1] - 1;
if (!item_list[index])
item_list[index] = Object.create(null);
item_list[index][matched[2] || 'action'] = value;
}
return item_list;
}
// parse {{Article history}}
function parse_Article_history_page(page_data, options) {
options = library_namespace.setup_options(options);
var item_list = [];
if (options.using_data) {
item_list.page_data = page_data;
} else if (page_data.title) {
// normalized page title
item_list.page_title = page_data.title;
}
var parsed = get_parsed(page_data);
parsed.each('template', function(token) {
parse_Article_history_token(token, item_list);
// return to_exit;
});
return item_list;
}
// --------------------------------------------------------------------------------------------
// 模板處理功能。尤其是採用Lua的。
function template_functions_site_name(session, options) {
return options && options.site_name || wiki_API.site_name(session);
}
function token_is_invoke(token) {
return token.type === 'magic_word_function' && token.name === '#invoke'
// {{#invoke:Template:Delete2|CSD_reason|parent=yes}}
// using [[Module:Template:Delete2]]
&& token.module_name;
}
function get_function_config_of(template, options) {
if (!template)
return;
options = library_namespace.setup_options(options);
var session = wiki_API.session_of_options(options);
var site_name = template_functions_site_name(session, options);
var functions_of_site = template_functions.functions_of_site[site_name];
// console.trace([site_name, functions_of_site, options]);
var is_invoke, template_name;
if (typeof template === 'string') {
is_invoke = session ? session.is_namespace(template, 'Module')
: /^Module:/i.test(template);
template_name = template;
} else {
is_invoke = token_is_invoke(template);
template_name = is_invoke ? 'Module:' + template.module_name
: template.name;
}
function get_function_config() {
return functions_of_site && functions_of_site[template_name]
|| template_functions.functions_of_all_sites[template_name];
}
// template_processor
var function_config = get_function_config();
if (function_config)
return function_config;
if (!session || options.no_normalize) {
return;
}
// normalize_template_name() to redirect target
if (!is_invoke)
template_name = session.to_namespace(template_name, 'Template');
var redirect_target = session
.redirect_target_of(template_name, options);
if (false && template_name === 'Template:T') {
console.trace([ redirect_target, template_name, function_config,
site_name, functions_of_site,
template_functions.functions_of_all_sitesoptions ]);
}
if (redirect_target !== template_name && template.type) {
// template.redirect_target = redirect_target;
}
template_name = is_invoke ? redirect_target : session.remove_namespace(
redirect_target, options);
// console.trace([ template_name, get_function_config() ]);
return get_function_config();
}
// TODO: use adopter, adopt_function
function adapt_function(template_token, index, parent, options) {
if (!parent && !options && typeof index === 'object') {
// CeL.wiki.template_functions.adapt_function(token, options);
options = index;
index = template_token.index;
parent = template_token.parent;
}
if (!template_token || template_token.type !== 'transclusion'
&& !token_is_invoke(template_token)) {
return;
}
// template_processor()
var function_config = get_function_config_of(template_token, options);
// console.trace(function_config);
if (false && template_token.name === 'T') {
console.trace([ template_token, function_config ]);
}
if (!function_config) {
return;
}
if (function_config.properties) {
// 為 template_token 加上 template_functions 的屬性。
// 一些僅設定 token.expand() 的 parse 函數可採此法,設定成:
// Template_name:{properties:{expand:expand_template_Template_name}},
Object.assign(template_token, function_config.properties);
}
if (function_config.adapter) {
function_config = function_config.adapter;
}
if (typeof function_config === 'function')
return function_config(template_token, index, parent, options);
}
function set_proto_properties(template_name, template_properties, options) {
options = library_namespace.setup_options(options);
var session = wiki_API.session_of_options(options);
var site_name = template_functions_site_name(session, options);
var functions_of_site = template_functions.functions_of_site[site_name];
if (!functions_of_site)
template_functions.functions_of_site[site_name] = functions_of_site = Object
.create(null);
var function_config = functions_of_site[template_name];
if (!function_config) {
functions_of_site[template_name] = function_config = Object
.create(null);
} else if (typeof function_config === 'function') {
functions_of_site[template_name] = function_config = {
adapter : function_config
};
}
// assert: library_namespace.is_Object(function_config)
if (!function_config.properties) {
function_config.properties = Object.create(null);
}
Object.assign(function_config.properties, template_properties);
// console.trace(function_config.properties);
}
// ------------------------------------------
var KEY_dependent_on = typeof Symbol === 'function' ? Symbol('KEY_dependent_on')
: '\0dependent on';
// dependency_hash[site_name] = [ sites dependent on site_name ]
var dependency_hash = Object.create(null);
// loaded_sites = { 已經處理過的 site_name }
var loaded_sites = Object.create(null);
// 檢查所有依賴於 site_name_loaded 的。
function initialize_functions_of_site(site_name_loaded) {
// console.trace(dependency_hash);
var dependency_list = dependency_hash[site_name_loaded];
if (!dependency_list) {
return;
}
// free
delete dependency_hash[site_name_loaded];
dependency_list.forEach(function(_site_name) {
// _site_name 依賴於 site_name_loaded。
var functions_of_site
//
= template_functions.functions_of_site[_site_name];
// console.trace(functions_of_site);
var dependent_on = functions_of_site
&& functions_of_site[KEY_dependent_on];
if (!dependent_on || dependent_on.some(function(__site_name) {
if (!(__site_name in loaded_sites)) {
// __site_name 所依賴的 __site_name 尚未 loaded。
// assert: __site_name is loading now
return true;
}
})) {
return;
}
// console.trace('All dependency loaded.');
dependent_on.forEach(function(__site_name) {
var _dependent_on
//
= template_functions.functions_of_site[__site_name];
if (!_dependent_on)
return;
var function_name_list = Object.keys(_dependent_on);
function_name_list.forEach(function(function_name) {
if (!(function_name in functions_of_site)) {
library_namespace.debug('設定 ' + _site_name + '.'
+ function_name + '=' + __site_name + '.'
+ function_name, 2,
'initialize_functions_of_site');
functions_of_site[function_name]
// 不覆蓋已經存在的 function。
= _dependent_on[function_name];
}
})
});
});
}
function to_full_template_name(template_name, options) {
var session = wiki_API.session_of_options(options);
return session.is_namespace(template_name, 'Module') ? template_name
: session.to_namespace(template_name, 'Template');
}
// 糾正 functions_of_site 之模板名稱至重定向標的。
function correct_template_name(functions_of_site, options) {
var session = wiki_API.session_of_options(options);
// console.trace(functions_of_site);
for (var template_name_list = Object.keys(functions_of_site), index = 0; index < template_name_list.length; index++) {
var template_name = template_name_list[index];
var full_name = to_full_template_name(template_name, options);
var target_full_name = session.redirect_target_of(full_name);
if (target_full_name === full_name)
continue;
if (session.is_namespace(full_name, 'Template')) {
target_full_name = session.remove_namespace(target_full_name,
options);
}
if (functions_of_site[target_full_name]) {
if (functions_of_site[target_full_name] !== functions_of_site[template_name]) {
library_namespace.warn('correct_template_name: '
+ 'Copy configuration from ['
+ to_full_template_name(template_name, options)
+ '] to ['
+ to_full_template_name(target_full_name, options)
+ '] failed: Target exists.');
if (false) {
console
.trace([
to_full_template_name(template_name,
options),
functions_of_site[template_name],
to_full_template_name(target_full_name,
options),
functions_of_site[target_full_name] ]);
}
}
continue;
}
library_namespace.info('correct_template_name: '
+ 'Copy configuration from ['
+ to_full_template_name(template_name, options) + '] to ['
+ to_full_template_name(target_full_name, options) + ']');
functions_of_site[target_full_name] = functions_of_site[template_name];
}
}
// @inner
function initialize_session_template_functions(site_name, callback) {
var session = this;
var this_site_name = template_functions_site_name(session);
site_name = site_name || template_functions_site_name(session);
if (Array.isArray(site_name)) {
site_name.forEach(function(_site_name) {
initialize_session_template_functions
//
.call(session, _site_name);
});
callback && callback.call(session);
return;
}
// assert: typeof site_name === 'string'
// --------------------------------------
// 登記好 site_name 以供 initialize_functions_of_site() 使用
loaded_sites[site_name] = true;
// --------------------------------------
if (library_namespace.is_debug()) {
library_namespace.info('initialize_session_template_functions: '
+ 'register redirects of ' + site_name);
}
var function_name_list = Object
.keys(template_functions.functions_of_all_sites);
var functions_of_site = template_functions.functions_of_site[site_name];
if (functions_of_site) {
function_name_list.append(Object.keys(functions_of_site));
}
function_name_list.append(template_functions.biographical_templates);
// console.trace(function_name_list);
function_name_list.forEach(function(template_name) {
var normalized_name = session.normalize_title(template_name);
if (session.is_namespace(template_name, 'Template')
//
? template_name !== normalized_name : session
.remove_namespace(template_name) !== session
.remove_namespace(normalized_name)) {
library_namespace.error([
'initialize_session_template_functions: ',
{
// gettext_config:{"id":"must-rename-$1-to-$2-to-work"}
T : [ '%1 必須更名為 %2 才能起作用!',
JSON.stringify(template_name),
JSON.stringify(normalized_name) ]
} ]);
}
});
// assert: (function_name_list.length > 0),
// because template_functions.biographical_templates.length > 0
function_name_list = function_name_list.map(function(name) {
return to_full_template_name(name, session);
});
// console.trace([ site_name, function_name_list, functions_of_site ]);
session.register_redirects(function_name_list, function() {
// console.trace(site_name);
correct_template_name(template_functions.functions_of_all_sites,
session);
if (functions_of_site)
correct_template_name(functions_of_site, session);
session.biographical_templates
//
= session.redirect_target_of(
//
template_functions.biographical_templates);
// console.trace(session.biographical_templates);
}, {
// namespace : 'Template',
no_message : true
});
// --------------------------------------
initialize_functions_of_site(site_name);
// --------------------------------------
var dependent_on = functions_of_site
&& functions_of_site[KEY_dependent_on];
if (!dependent_on) {
callback && callback.call(session);
return;
}
if (library_namespace.is_debug()) {
library_namespace.info('initialize_session_template_functions: 設定 '
+ site_name + ' 採用 ' + dependent_on + ' 的模板特設功能。');
}
(Array.isArray(dependent_on) ? dependent_on : [ dependent_on ])
//
.forEach(function(_site_name) {
if (!dependency_hash[_site_name])
dependency_hash[_site_name] = [];
dependency_hash[_site_name].push(site_name);
});
// console.trace(dependency_hash);
session.load_template_functions(dependent_on, callback, true);
}
var module_name = this.id;
function load_template_functions(site_name, callback) {
var session = this;
site_name = site_name || wiki_API.site_name(session);
if (!site_name
//
&& (site_name = session && session.latest_site_configurations
//
&& session.latest_site_configurations.general.lang)) {
site_name += 'wiki';
} else if (!site_name) {
throw new Error('load_template_functions: Cannot get site_name!');
}
var submodules = Array.isArray(site_name) ? site_name : [ site_name ];
submodules = submodules.map(function(_site_name) {
if (!_site_name
// have been loaded
|| template_functions.functions_of_site[_site_name]) {
return;
}
return library_namespace.to_module_name(module_name
//
+ library_namespace.env.module_name_separator + _site_name);
});
// console.trace([ site_name, submodules ]);
// 注意: 若已設定 `CeL.application.net.wiki.template_functions.zhwiki`,
// 則不能採用匯添加 prefix `library_namespace.Class` 的
// library_namespace.to_module_name(),否則會被忽略,不載入!
library_namespace.run(submodules, initialize_session_template_functions
.bind(session, site_name, callback));
}
// --------------------------------------------------------------------------------------------
// TODO:?
// template_functions[KEY_subdomain_for_project][site_name]
// var KEY_subdomain_for_project;
// template_functions[KEY_common_templates].template_name
// var KEY_common_templates;
// export 導出.
library_namespace.set_method(wiki_API.prototype, {
load_template_functions : load_template_functions
});
// wiki_API.parser.parser_prototype 會被複製到 instance 上。
// 採用 library_namespace.set_method() 會無法列舉與複製,會出問題。
Object.assign(wiki_API.parser.parser_prototype, {
is_biography : page_is_biography
});
// console.trace(wiki_API.parser.parser_prototype.is_biography);
Object.assign(template_functions, {
/**
* functions configuration of site <code>
functions_of_site[site_name] = {
template_name : __template_configuration__
}
__template_configuration__ = function adapter() {};
// 將會轉成:
__template_configuration__ = { adapter : function adapter() {} };
// 一般性:
__template_configuration__ = {
// TODO: 切分 adapter,m parser
// adapter 兼 parser
adapter : function parse_template_token(template_token, index, parsent, options) {
// parse and create corresponding attributes
token.property = ...
// https://www.mediawiki.org/w/api.php?action=help&modules=expandtemplates
token.expand = expandtemplates
},
// @see adapt_function
properties : {
expand : function expand(options) {}
}
};
<code>
*/
functions_of_site : Object.create(null),
functions_of_all_sites : Object.create(null),
// wiki_API.template_functions.KEY_dependent_on
// e.g., zhmoegirl 設定 dependent on [ 'zhwiki' ]
// 必須是模板名稱不可能使用到的 key 值。
KEY_dependent_on : KEY_dependent_on,
adapt_function : adapt_function,
set_proto_properties : set_proto_properties,
// ----------------------------
parse_conversions : parse_conversions,
// ----------------------------
Hat : {
names : Hat_names,
text_of : text_of_Hat_flag,
result_includes : Hat_flag_result_includes,
parse : parse_Hat
},
Old_vfd_multi : {
names : Old_vfd_multi__names,
main_name : Old_vfd_multi__main_name,
/**
* @example<code>
const item_list = CeL.wiki.template_functions.Old_vfd_multi.parse_page(page_data, {
unique: true,
additional_parameters
});
</code>
*/
parse_page : parse_Old_vfd_multi_page,
unique_item_list : Old_vfd_multi__unique_item_list,
item_list_to_object : Old_vfd_multi__item_list_to_template_object,
item_list_to_wikitext : Old_vfd_multi__item_list_to_wikitext,
replace_by : replace_Old_vfd_multi,
text_of : text_of_Hat_flag
},
Article_history : {
names : Article_history__names,
parse_page : parse_Article_history_page
},
Multidel : {
names : Multidel_names
}
});
// 等執行再包含入必須的模組。
this.finish = function(name_space, waiting, sub_modules_to_full_module_path) {
// general_functions 必須在個別 wiki profiles 之前載入。
// 如 CeL.application.net.wiki.template_functions.jawiki 依賴於
// general_functions!
library_namespace.run(
sub_modules_to_full_module_path('general_functions'), waiting);
return waiting;
};
return template_functions;
}