cejs
Version:
A JavaScript module framework that is simple to use.
486 lines (424 loc) • 16.2 kB
JavaScript
/**
* @name CeL function for MediaWiki (Wikipedia / 維基百科): 特色內容特設功能。
*
* 注意: 本程式庫必須應各wiki特色內容改動而改寫。
*
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
*
* @example <code>
CeL.run('application.net.wiki.featured_content');
wiki.get_featured_content('FFA', function(FC_data_hash) {});
wiki.get_featured_content('GA', function(FC_data_hash) {});
wiki.get_featured_content('FA', function(FC_data_hash) {});
wiki.get_featured_content('FL', function(FC_data_hash) {});
</code>
*
* @since 2020/1/22 9:18:43
*/
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
;
// 'use asm';
// --------------------------------------------------------------------------------------------
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
typeof CeL === 'function' && CeL.run({
// module name
name : 'application.net.wiki.featured_content',
require : 'data.native.' + '|application.net.wiki.'
// load MediaWiki module basic functions
+ '|application.net.wiki.namespace.'
// for to_exit
+ '|application.net.wiki.parser.'
//
+ '|application.net.wiki.page.|application.net.wiki.list.',
// 設定不匯出的子函式。
no_extend : 'this,*',
// 為了方便格式化程式碼,因此將 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 featured_content() {
}
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;
}
// ------------------------------------------------------------------------
/** 特色內容為列表 */
var KEY_IS_LIST = 'is_list';
/** 為已撤銷的特色內容 */
var KEY_ISFFC = 'is_former';
/** 特色內容類別 */
var KEY_CATEGORY = 'category';
/** 指示用。會在 parse_each_zhwiki_FC_item_list_page() 之後就刪除。 */
var KEY_LIST_PAGE = 'list page';
function remove_KEY_LIST_PAGE(FC_data_hash) {
for ( var title in FC_data_hash) {
delete FC_data_hash[title][KEY_LIST_PAGE];
}
}
var featured_content_configurations = {
zhwiki : {
// @see [[Category:特色内容]]
list_source : {
FA : '典范条目',
FL : '特色列表',
FP : '特色图片',
GA : '優良條目',
},
get_FC : /* get_zhwiki_FC_via_list_page */get_FC_via_category
},
jawiki : {
// @see [[ja:Category:記事の選考]]
list_source : {
FA : 'ウィキペディア 秀逸な記事',
FL : 'ウィキペディア 秀逸な一覧',
FP : 'ウィキペディア 秀逸な画像',
GA : 'ウィキペディア 良質な記事'
},
get_FC : get_FC_via_category
},
enwiki : {
// @see [[en:Category:Featured content]]
list_source : {
FFA : {
page : 'Wikipedia:Former featured articles',
handler : parse_enwiki_FFA
},
DGA : 'Delisted good articles',
FA : 'Featured articles',
FL : 'Featured lists',
FP : 'Featured pictures',
FT : 'Featured topics',
GA : 'Good articles'
},
get_FC : get_FC_via_category
}
};
function get_site_configurations(session) {
// e.g., 'zhwiki'
var site_name = wiki_API.site_name(session);
var FC_configurations = featured_content_configurations[site_name];
return FC_configurations;
}
// @see 20190101.featured_content_maintainer.js
// 注意: 這邊尚未處理 redirects 的問題!!
function parse_each_zhwiki_FC_item_list_page(page_data, redirects_to_hash,
sub_FC_list_pages) {
var using_GA = options.type === 'GA';
/** {String}將顯示的類型名稱。 */
var TYPE_NAME = using_GA ? '優良條目' : '特色內容';
/** {Array}錯誤記錄 */
var error_logs = [];
var FC_data_hash = this.FC_data_hash
// FC_data_hash[redirected FC_title] = { FC_data }
|| (this.FC_data_hash = Object.create(null));
/**
* {String}page title = page_data.title
*/
var title = wiki_API.title_of(page_data);
/**
* {String}page content, maybe undefined. 條目/頁面內容 =
* wiki_API.revision_content(revision)
*/
var content = wiki_API.content_of(page_data);
//
var matched;
/** 特色內容為列表 */
var is_list = /list|列表/.test(title)
// e.g., 'Wikipedia:FL'
|| /:[DF]?[FG]L/.test(page_data.original_title || title),
// 本頁面為已撤消的條目列表。注意: 這包含了被撤銷後再次被評為典範的條目。
is_FFC = [ page_data.original_title, title ].join('|');
// 對於進階的條目,採用不同的 is_FFC 表示法。
is_FFC = using_GA && /:FF?A/.test(is_FFC) && 'UP'
|| /:[DF][FG][AL]|已撤消的|已撤销的/.test(is_FFC);
if (is_FFC) {
// 去掉被撤銷後再次被評為典範的條目/被撤銷後再次被評為特色的列表/被撤銷後再次被評選的條目
content = content.replace(/\n== *(?:被撤銷後|被撤销后)[\s\S]+$/, '');
}
// 自動偵測要使用的模式。
function test_pattern(pattern, min) {
var count = 0, matched;
while (matched = pattern.exec(content)) {
if (matched[1] && count++ > (min || 20)) {
return pattern;
}
}
}
var catalog,
// matched: [ all, link title, display, catalog ]
PATTERN_Featured_content = test_pattern(
// @see [[Template:FA number]] 被標記為粗體的條目已經在作為典範條目時在首頁展示過
// 典範條目, 已撤銷的典範條目, 已撤销的特色列表: '''[[title]]'''
// @see PATTERN_category
/'''\[\[([^{}\[\]\|<>\t\n#�]+)(?:\|([^\[\]\|�]*))?\]\]'''|\n==([^=].*?)==\n/g)
// 特色列表: [[:title]]
|| test_pattern(/\[\[:([^{}\[\]\|<>\t\n#�]+)(?:\|([^\[\]\|�]*))?\]\]|\n==([^=].*?)==\n/g)
// 優良條目轉換到子頁面模式: 警告:本頁中的所有嵌入頁面都會被機器人當作優良條目的分類列表。請勿嵌入非優良條目的分類列表。
|| test_pattern(/{{(Wikipedia:[^{}\|]+)}}/g, 10)
// 優良條目子分類列表, 已撤消的優良條目: all links NOT starting with ':'
|| /\[\[([^{}\[\]\|<>\t\n#�:][^{}\[\]\|<>\t\n#�]*)(?:\|([^\[\]\|�]*))?\]\]|\n===([^=].*?)===\n/g;
library_namespace.log(wiki_API.title_link_of(title)
+ ': '
+ (is_FFC ? 'is former'
+ (is_FFC === true ? '' : ' (' + is_FFC + ')')
: 'NOT former') + ', '
+ (is_list ? 'is list' : 'is article') + ', using pattern '
+ PATTERN_Featured_content);
// reset pattern
PATTERN_Featured_content.lastIndex = 0;
// 分類/類別。
if (matched = title.match(/\/(?:分類|分类)\/([^\/]+)/)) {
catalog = matched[1];
}
if (false) {
library_namespace.log(content);
console.log([ page_data.original_title || title, is_FFC, is_list,
PATTERN_Featured_content ]);
}
while (matched = PATTERN_Featured_content.exec(content)) {
// 還沒繁簡轉換過的標題。
var original_FC_title = wiki_API.normalize_title(matched[1]);
if (matched.length === 2) {
sub_FC_list_pages.push(original_FC_title);
continue;
}
// assert: matched.length === 4
if (matched[3]) {
// 分類/類別。
catalog = matched[3].replace(/<\!--[\s\S]*?-->/g, '').trim()
.replace(/\s*(\d+)$/, '');
continue;
}
// 去除並非文章,而是工作連結的情況。 e.g., [[File:文件名]], [[Category:维基百科特色内容|*]]
if (this.namespace(original_FC_title, 'is_page_title') !== 0) {
continue;
}
// 轉換成經過繁簡轉換過的最終標題。
var FC_title = redirects_to_hash
&& redirects_to_hash[original_FC_title]
|| original_FC_title;
if (FC_title in FC_data_hash) {
// 基本檢測與提醒。
if (FC_data_hash[FC_title][KEY_ISFFC] === is_FFC) {
library_namespace.warn(
//
'parse_each_zhwiki_FC_item_list_page: Duplicate '
+ TYPE_NAME + ' title: ' + FC_title + '; '
+ JSON.stringify(FC_data_hash[FC_title]) + '; '
+ matched[0]);
error_logs.push(wiki_API.title_link_of(title)
+ '有重複條目: '
+ wiki_API.title_link_of(original_FC_title)
+ (original_FC_title === FC_title ? '' : ', '
+ wiki_API.title_link_of(FC_title)));
} else if (!!FC_data_hash[FC_title][KEY_ISFFC] !== !!is_FFC
&& (FC_data_hash[FC_title][KEY_ISFFC] !== 'UP' || is_FFC !== false)) {
error_logs
.push(wiki_API.title_link_of(FC_title)
+ ' 被同時列在了現存及已撤銷的'
+ TYPE_NAME
+ '清單中: '
+ wiki_API.title_link_of(original_FC_title)
+ '@'
+ wiki_API.title_link_of(title)
+ ', '
+ wiki_API
.title_link_of(FC_data_hash[FC_title][KEY_LIST_PAGE][1])
+ '@'
+ wiki_API
.title_link_of(FC_data_hash[FC_title][KEY_LIST_PAGE][0]));
library_namespace.error(wiki_API.title_link_of(FC_title)
+ ' 被同時列在了現存及已撤銷的' + TYPE_NAME + '清單中: ' + is_FFC
+ '; ' + JSON.stringify(FC_data_hash[FC_title]));
}
}
var FC_data = FC_data_hash[FC_title] = Object.create(null);
FC_data[KEY_IS_LIST] = is_list;
FC_data[KEY_ISFFC] = is_FFC;
if (catalog)
FC_data[KEY_CATEGORY] = catalog;
FC_data[KEY_LIST_PAGE] = [ title, original_FC_title ];
}
return error_logs;
}
function get_zhwiki_FC_via_list_page(options, callback) {
var session = this;
var using_GA = options.type === 'GA';
var FC_list_pages = (using_GA ? 'WP:GA' : 'WP:FA|WP:FL').split('|');
var Former_FC_list_pages = (using_GA ? 'WP:DGA|WP:FA|WP:FFA'
: 'WP:FFA|WP:FFL').split('|');
var page_options = {
redirects : 1,
multi : true
};
this.page(FC_list_pages.concat(Former_FC_list_pages), function(
page_data_list) {
var sub_FC_list_pages = [];
page_data_list.forEach(function(page_data) {
parse_each_zhwiki_FC_item_list_page.call(session, page_data,
options.redirects_to_hash, sub_FC_list_pages);
});
if (sub_FC_list_pages.length === 0) {
remove_KEY_LIST_PAGE(session.FC_data_hash);
callback && callback(session.FC_data_hash);
return;
}
session.page(sub_FC_list_pages, function(page_data_list) {
page_data_list.forEach(function(page_data) {
parse_each_zhwiki_FC_item_list_page.call(session,
page_data, options.redirects_to_hash);
});
remove_KEY_LIST_PAGE(session.FC_data_hash);
callback && callback(session.FC_data_hash);
}, page_options);
}, page_options);
}
// ------------------------------------------------------------------------
function parse_enwiki_FFA(page_data, type_name) {
/**
* {String}page content, maybe undefined. 條目/頁面內容 =
* wiki_API.revision_content(revision)
*/
var content = wiki_API.content_of(page_data);
content = content.replace(/^[\s\S]+?\n(==.+?==)/, '$1')
// remove == Former featured articles that have been re-promoted ==
.replace(/==\s*Former featured articles.+?==[\s\S]*$/, '');
var FC_data_hash = this.FC_data_hash;
var PATTERN_Featured_content = /\[\[(.+?)\]\]/g, matched;
while (matched = PATTERN_Featured_content.exec(content)) {
var FC_title = matched[1];
var FC_data = FC_data_hash[FC_title];
if (FC_data) {
if (!FC_data.types.includes(type_name)) {
// 把重要的放在前面。
FC_data.types.unshift(type_name);
}
// Do not overwrite
continue;
}
FC_data = FC_data_hash[FC_title] = {
type : type_name,
types : [ type_name ]
};
FC_data[KEY_ISFFC] = true;
// FC_data[KEY_IS_LIST] = is_list;
}
}
// ------------------------------------------------------------------------
function normalize_type_name(type) {
return type;
}
function get_FC_via_category(options, callback) {
var FC_configurations = get_site_configurations(this);
var type_name = normalize_type_name(options.type);
var list_source = FC_configurations.list_source[type_name];
// console.trace([ FC_configurations, type_name, list_source ]);
if (!list_source) {
// library_namespace.error('get_FC_via_category: ' + error);
callback && callback(null, !options.ignore_missed && new Error('Unknown type: ' + options.type));
return;
}
// ----------------------------
var FC_data_hash = this.FC_data_hash
// FC_data_hash[redirected FC_title] = { FC_data }
|| (this.FC_data_hash = Object.create(null));
// ----------------------------
var session = this;
if (list_source.page) {
this.page(list_source.page, function(page_data) {
list_source.handler.call(session, page_data, type_name);
callback && callback(FC_data_hash);
});
return;
}
// ----------------------------
var category_title = list_source;
/** 特色內容為列表 */
var is_list = /list|列表/.test(category_title);
wiki_API.list(category_title, function(list/* , target, options */) {
list.forEach(function(page_data) {
var FC_title = page_data.title;
var FC_data = FC_data_hash[FC_title];
if (!FC_data) {
FC_data = FC_data_hash[FC_title] = {
type : type_name,
types : [ type_name ]
};
} else if (FC_data.type !== type_name) {
if (FC_data.type !== 'FFA' || type_name === 'FA') {
if (options.on_conflict) {
options.on_conflict(FC_title, {
from : FC_data.type,
to : type_name,
category : category_title
});
} else {
library_namespace.warn('get_FC_via_category: '
+ FC_title + ': ' + FC_data.type + '→'
+ type_name);
}
}
if (!FC_data.types.includes(type_name)) {
// 把重要的放在前面。
FC_data.types.unshift(type_name);
}
FC_data.type = type_name;
}
FC_data[KEY_IS_LIST] = is_list;
// FC_data[KEY_ISFFC] = false;
// if (catalog) FC_data[KEY_CATEGORY] = catalog;
});
callback && callback(FC_data_hash);
}, {
// [KEY_SESSION]
session : this,
// namespace: '0|1',
type : 'categorymembers'
});
}
// --------------------------------------------------------------------------------------------
// export 導出.
// Object.assign(featured_content, {});
// ------------------------------------------------------------------------
// wrapper for local function
wiki_API.prototype.get_featured_content_configurations = function get_featured_content_configurations() {
return get_site_configurations(this);
};
// callback(wiki.FC_data_hash);
// e.g.,
// wiki.FC_data_hash[title]={type:'GA',types:['GA','FFA'],is_former:true,is_list:false}
wiki_API.prototype.get_featured_content = function get_featured_content(
options, callback) {
var FC_configurations = this.get_featured_content_configurations();
var get_FC_function = FC_configurations && FC_configurations.get_FC;
if (!get_FC_function) {
library_namespace.error('get_featured_content: '
+ 'Did not configured how to get featured content! '
+ wiki_API.site_name(this));
return;
}
if (typeof options === 'string') {
options = {
type : options
};
} else {
options = library_namespace.setup_options(options);
}
get_FC_function.call(this, options, callback);
};
// 不設定(hook)本 module 之 namespace,僅執行 module code。
return library_namespace.env.not_to_extend_keyword;
// return featured_content;
}