mirror of
https://scm.univ-tours.fr/22107988t/rappaurio-sae501_502.git
synced 2025-08-29 18:05:57 +02:00
permet l'ajout des frameworks et des routes
This commit is contained in:
412
app/node_modules/cejs/application/net/wiki/Flow.js
generated
vendored
Normal file
412
app/node_modules/cejs/application/net/wiki/Flow.js
generated
vendored
Normal file
@@ -0,0 +1,412 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科): Flow, Structured
|
||||
* Discussions
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* TODO:<code>
|
||||
|
||||
|
||||
</code>
|
||||
*
|
||||
* @since 2019/10/11 拆分自 CeL.application.net.wiki
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.Flow',
|
||||
|
||||
require : 'data.native.' + '|application.net.wiki.'
|
||||
// load MediaWiki module basic functions
|
||||
+ '|application.net.wiki.namespace.'
|
||||
//
|
||||
+ '|application.net.wiki.query.',
|
||||
|
||||
// 設定不匯出的子函式。
|
||||
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;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Flow page support. Flow 功能支援。
|
||||
// [[mediawikiwiki:Extension:Flow/API]]
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=flow
|
||||
|
||||
// https://zh.wikipedia.org/w/api.php?action=query&prop=flowinfo&titles=Wikipedia_talk:Flow_tests
|
||||
// https://zh.wikipedia.org/w/api.php?action=query&prop=info&titles=Wikipedia_talk:Flow_tests
|
||||
// https://zh.wikipedia.org/w/api.php?action=flow&submodule=view-topiclist&page=Wikipedia_talk:Flow_tests&vtlformat=wikitext&utf8=1
|
||||
// .roots[0]
|
||||
// https://zh.wikipedia.org/w/api.php?action=flow&submodule=view-topic&page=Topic:sqs6skdav48d3xzn&vtformat=wikitext&utf8=1
|
||||
|
||||
// https://www.mediawiki.org/w/api.php?action=flow&submodule=view-header&page=Talk:Sandbox&vhformat=wikitext&utf8=1
|
||||
// https://www.mediawiki.org/w/api.php?action=flow&submodule=view-topiclist&utf8=1&page=Talk:Sandbox
|
||||
|
||||
/**
|
||||
* get the infomation of Flow.
|
||||
*
|
||||
* @param {String|Array}title
|
||||
* page title 頁面標題。可為話題id/頁面標題+話題標題。<br />
|
||||
* {String}title or [ {String}API_URL, {String}title or
|
||||
* {Object}page_data ]
|
||||
* @param {Function}callback
|
||||
* 回調函數。 callback({Object}page_data)
|
||||
* @param {Object}[options]
|
||||
* 附加參數/設定選擇性/特殊功能與選項
|
||||
*/
|
||||
function Flow_info(title, callback, options) {
|
||||
var action = normalize_title_parameter(title, options);
|
||||
if (!action) {
|
||||
throw 'Flow_info: Invalid title: ' + wiki_API.title_link_of(title);
|
||||
}
|
||||
|
||||
// [[mw:Extension:StructuredDiscussions/API#Detection]]
|
||||
// 'prop=flowinfo' is deprecated. use 'action=query&prop=info'.
|
||||
// The content model will be 'flow-board' if it's enabled.
|
||||
action[1] = 'action=query&prop=info&' + action[1];
|
||||
|
||||
wiki_API.query(action, typeof callback === 'function'
|
||||
//
|
||||
&& function(data) {
|
||||
if (library_namespace.is_debug(2)
|
||||
// .show_value() @ interact.DOM, application.debug
|
||||
&& library_namespace.show_value)
|
||||
library_namespace.show_value(data, 'Flow_info: data');
|
||||
|
||||
var error = data && data.error;
|
||||
// 檢查伺服器回應是否有錯誤資訊。
|
||||
if (error) {
|
||||
library_namespace.error('Flow_info: ['
|
||||
//
|
||||
+ error.code + '] ' + error.info);
|
||||
/**
|
||||
* e.g., Too many values supplied for parameter 'pageids': the
|
||||
* limit is 50
|
||||
*/
|
||||
if (data.warnings
|
||||
//
|
||||
&& data.warnings.query && data.warnings.query['*'])
|
||||
library_namespace.warn(data.warnings.query['*']);
|
||||
callback(data, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data || !data.query || !data.query.pages) {
|
||||
library_namespace.warn('Flow_info: Unknown response: ['
|
||||
//
|
||||
+ (typeof data === 'object'
|
||||
//
|
||||
&& typeof JSON !== 'undefined'
|
||||
//
|
||||
? JSON.stringify(data) : data) + ']');
|
||||
if (library_namespace.is_debug()
|
||||
// .show_value() @ interact.DOM, application.debug
|
||||
&& library_namespace.show_value)
|
||||
library_namespace.show_value(data);
|
||||
callback(null, data);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: data.query.normalized=[{from:'',to:''},...]
|
||||
|
||||
data = data.query.pages;
|
||||
var pages = [];
|
||||
for ( var pageid in data) {
|
||||
var page = data[pageid];
|
||||
pages.push(page);
|
||||
}
|
||||
|
||||
// options.multi: 即使只取得單頁面,依舊回傳 Array。
|
||||
if (!options || !options.multi)
|
||||
if (pages.length <= 1) {
|
||||
if (pages = pages[0])
|
||||
pages.is_Flow = is_Flow(pages);
|
||||
library_namespace.debug('只取得單頁面 [[' + pages.title
|
||||
//
|
||||
+ ']],將回傳此頁面資料,而非 Array。', 2, 'Flow_info');
|
||||
} else {
|
||||
library_namespace.debug('Get ' + pages.length
|
||||
//
|
||||
+ ' page(s)! The pages'
|
||||
//
|
||||
+ ' will all passed to callback as Array!'
|
||||
//
|
||||
, 2, 'Flow_info');
|
||||
}
|
||||
|
||||
/**
|
||||
* page 之 structure 將按照 wiki API 本身之 return!<br />
|
||||
* <code>
|
||||
page_data = {ns,title,missing:'']}
|
||||
page_data = {pageid,ns,title,flowinfo:{flow:[]}}
|
||||
page_data = {pageid,ns,title,flowinfo:{flow:{enabled:''}}}
|
||||
* </code>
|
||||
*/
|
||||
callback(pages);
|
||||
}, null, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 檢測 page_data 是否為 Flow 討論頁面系統。
|
||||
*
|
||||
* other contentmodel: "MassMessageListContent"
|
||||
*
|
||||
* @param {Object}page_data
|
||||
* page data got from wiki API.
|
||||
*
|
||||
* @returns {Boolean}是否為 Flow 討論頁面。
|
||||
*/
|
||||
function is_Flow(page_data) {
|
||||
if ('contentmodel' in page_data) {
|
||||
// used in prop=info
|
||||
return page_data.contentmodel === 'flow-board';
|
||||
}
|
||||
|
||||
var flowinfo = page_data &&
|
||||
// wiki_API.is_page_data(page_data) &&
|
||||
page_data.flowinfo;
|
||||
if (flowinfo) {
|
||||
// used in prop=flowinfo (deprecated)
|
||||
// flowinfo:{flow:{enabled:''}}
|
||||
return flowinfo.flow && ('enabled' in flowinfo.flow);
|
||||
}
|
||||
|
||||
// e.g., 從 wiki_API.page 得到的 page_data
|
||||
if (page_data = wiki_API.content_of.revision(page_data))
|
||||
return (page_data.contentmodel || page_data.slots
|
||||
&& page_data.slots.main
|
||||
&& page_data.slots.main.contentmodel) === 'flow-board';
|
||||
}
|
||||
|
||||
/** {Object}abbreviation 縮寫 */
|
||||
var Flow_abbreviation = {
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=flow%2Bview-header
|
||||
// 關於討論板的描述。使用 .revision
|
||||
header : 'h',
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=flow%2Bview-topiclist
|
||||
// 討論板話題列表。使用 .revisions
|
||||
topiclist : 'tl'
|
||||
};
|
||||
|
||||
/**
|
||||
* get topics of the page.
|
||||
*
|
||||
* @param {String|Array}title
|
||||
* page title 頁面標題。可為話題id/頁面標題+話題標題。 {String}title or [
|
||||
* {String}API_URL, {String}title or {Object}page_data ]
|
||||
* @param {Function}callback
|
||||
* 回調函數。 callback({Object}topiclist)
|
||||
* @param {Object}[options]
|
||||
* 附加參數/設定選擇性/特殊功能與選項
|
||||
*/
|
||||
function Flow_page(title, callback, options) {
|
||||
// 處理 [ {String}API_URL, {String}title or {Object}page_data ]
|
||||
if (!is_api_and_title(title)) {
|
||||
title = [ options[KEY_SESSION] && options[KEY_SESSION].API_URL,
|
||||
title ];
|
||||
}
|
||||
|
||||
var page_data;
|
||||
if (wiki_API.is_page_data(title[1]))
|
||||
page_data = title[1];
|
||||
|
||||
title[1] = 'page=' + encodeURIComponent(wiki_API.title_of(title[1]));
|
||||
|
||||
if (options && options.redirects) {
|
||||
// 舊版毋須 '&redirects=1','&redirects' 即可。
|
||||
title[1] += '&redirects=1';
|
||||
}
|
||||
|
||||
// e.g., { flow_view : 'header' }
|
||||
var view = options && options.flow_view
|
||||
//
|
||||
|| Flow_page.default_flow_view;
|
||||
title[1] = 'action=flow&submodule=view-' + view + '&v'
|
||||
+ (Flow_abbreviation[view] || view.charAt(0).toLowerCase())
|
||||
+ 'format=' + (options && options.format || 'wikitext') + '&'
|
||||
+ title[1];
|
||||
|
||||
if (!title[0])
|
||||
title = title[1];
|
||||
|
||||
wiki_API.query(title, typeof callback === 'function'
|
||||
//
|
||||
&& function(data) {
|
||||
if (library_namespace.is_debug(2)
|
||||
// .show_value() @ interact.DOM, application.debug
|
||||
&& library_namespace.show_value)
|
||||
library_namespace.show_value(data, 'Flow_page: data');
|
||||
|
||||
var error = data && data.error;
|
||||
// 檢查伺服器回應是否有錯誤資訊。
|
||||
if (error) {
|
||||
library_namespace.error(
|
||||
//
|
||||
'Flow_page: [' + error.code + '] ' + error.info);
|
||||
callback(page_data);
|
||||
return;
|
||||
}
|
||||
|
||||
// data =
|
||||
// { flow: { 'view-topiclist': { result: {}, status: 'ok' } } }
|
||||
if (!(data = data.flow)
|
||||
//
|
||||
|| !(data = data['view-' + view]) || data.status !== 'ok') {
|
||||
library_namespace.error(
|
||||
//
|
||||
'Flow_page: Error status [' + (data && data.status) + ']');
|
||||
callback(page_data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (page_data)
|
||||
// assert: data.result = { ((view)) : {} }
|
||||
Object.assign(page_data, data.result);
|
||||
else
|
||||
page_data = data.result[view];
|
||||
callback(page_data);
|
||||
}, null, options);
|
||||
}
|
||||
|
||||
/** {String}default view to flow page */
|
||||
Flow_page.default_flow_view = 'topiclist';
|
||||
|
||||
/**
|
||||
* Create a new topic. 發新話題。 Reply to an existing topic.
|
||||
*
|
||||
* @param {String|Array}title
|
||||
* page title 頁面標題。 {String}title or [ {String}API_URL,
|
||||
* {String}title or {Object}page_data ]
|
||||
* @param {String}topic
|
||||
* 新話題的標題文字。 {String}topic
|
||||
* @param {String|Function}text
|
||||
* page contents 頁面內容。 {String}text or {Function}text(page_data)
|
||||
* @param {Object}token
|
||||
* login 資訊,包含“csrf”令牌/密鑰。
|
||||
* @param {Object}[options]
|
||||
* 附加參數/設定選擇性/特殊功能與選項
|
||||
* @param {Function}[callback]
|
||||
* 回調函數。 callback(title, error, result)
|
||||
*
|
||||
* @see https://www.mediawiki.org/w/api.php?action=help&modules=flow%2Bnew-topic
|
||||
* https://www.mediawiki.org/w/api.php?action=help&modules=flow%2Breply
|
||||
*/
|
||||
function edit_topic(title, topic, text, token, options, callback) {
|
||||
// console.log(text);
|
||||
if (library_namespace.is_thenable(text)) {
|
||||
text.then(function(text) {
|
||||
edit_topic(title, topic, text, token, options, callback);
|
||||
}, function(error) {
|
||||
callback(title, error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var action = 'action=flow';
|
||||
// 處理 [ {String}API_URL, {String}title or {Object}page_data ]
|
||||
if (Array.isArray(title)) {
|
||||
action = [ title[0], action ];
|
||||
title = title[1];
|
||||
} else if (options[KEY_SESSION]) {
|
||||
action = [ options[KEY_SESSION].API_URL, action ];
|
||||
}
|
||||
|
||||
if (wiki_API.is_page_data(title))
|
||||
title = title.title;
|
||||
// assert: typeof title === 'string' or title is invalid.
|
||||
if (title.length > 260) {
|
||||
// [nttopic] 話題標題已限制在 260 位元組內。
|
||||
// 自動評論與摘要的長度限制是260個字符。需要小心任何超出上述限定的東西將被裁剪掉。
|
||||
// 260 characters
|
||||
// https://github.com/wikimedia/mediawiki-extensions-Flow/blob/master/includes/Model/PostRevision.php
|
||||
// const MAX_TOPIC_LENGTH = 260;
|
||||
// https://github.com/wikimedia/mediawiki-extensions-Flow/blob/master/i18n/zh-hant.json
|
||||
library_namespace
|
||||
.warn('edit_topic: Title is too long and will be truncated: ['
|
||||
+ error.code + ']');
|
||||
title = title.slice(0, 260);
|
||||
}
|
||||
|
||||
// default parameters
|
||||
var _options = {
|
||||
// notification_name : 'flow',
|
||||
submodule : 'new-topic',
|
||||
page : title,
|
||||
nttopic : topic,
|
||||
ntcontent : text,
|
||||
ntformat : 'wikitext'
|
||||
};
|
||||
|
||||
edit_topic.copy_keys.forEach(function(key) {
|
||||
if (options[key])
|
||||
_options[key] = options[key];
|
||||
});
|
||||
|
||||
// the token should be sent as the last parameter.
|
||||
_options.token = library_namespace.is_Object(token) ? token.csrftoken
|
||||
: token;
|
||||
|
||||
wiki_API.query(action, typeof callback === 'function'
|
||||
//
|
||||
&& function(data) {
|
||||
if (library_namespace.is_debug(2)
|
||||
// .show_value() @ interact.DOM, application.debug
|
||||
&& library_namespace.show_value)
|
||||
library_namespace.show_value(data, 'edit_topic: data');
|
||||
|
||||
var error = data && data.error;
|
||||
// 檢查伺服器回應是否有錯誤資訊。
|
||||
if (error) {
|
||||
library_namespace.error('edit_topic: ['
|
||||
//
|
||||
+ error.code + '] ' + error.info);
|
||||
} else if (!(data = data.flow)
|
||||
//
|
||||
|| !(data = data['new-topic']) || data.status !== 'ok') {
|
||||
// data = { flow: { 'new-topic': { status: 'ok',
|
||||
// workflow: '', committed: {} } } }
|
||||
error = 'edit_topic: Bad status ['
|
||||
//
|
||||
+ (data && data.status) + ']';
|
||||
library_namespace.error(error);
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
// title.title === wiki_API.title_of(title)
|
||||
callback(title.title, error, data);
|
||||
}
|
||||
}, _options, options);
|
||||
}
|
||||
|
||||
/** {Array}欲 copy 至 Flow edit parameters 之 keys。 */
|
||||
edit_topic.copy_keys = 'summary|bot|redirect|nocreate'.split(',');
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
// CeL.wiki.Flow.*
|
||||
Object.assign(Flow_info, {
|
||||
is_Flow : is_Flow,
|
||||
page : Flow_page,
|
||||
edit : edit_topic
|
||||
});
|
||||
|
||||
return Flow_info;
|
||||
}
|
75
app/node_modules/cejs/application/net/wiki/README.wiki
generated
vendored
Normal file
75
app/node_modules/cejs/application/net/wiki/README.wiki
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<!-- The first line is blank due to BOM -->
|
||||
= CeJS MediaWiki module =
|
||||
MediaWiki 自動化作業程式庫,主要用於編寫[[維基百科:機器人]]。
|
||||
|
||||
|
||||
== Usage in [https://www.mediawiki.org/wiki/Manual:Interface/JavaScript mediawiki user script] (User:Example/common.js) ==
|
||||
<pre><code>
|
||||
if (!window.CeL) {
|
||||
window.CeL = { initializer: function() { CeL.run('application.net.wiki', CeL_initialization); } };
|
||||
mw.loader.load('https://kanasimi.github.io/CeJS/ce.js');
|
||||
}
|
||||
function CeL_initialization() {
|
||||
/** {Array} parsed page content */
|
||||
const parsed = CeL.wiki.parser('{{tl|t}}');
|
||||
parsed.each('template', function(token) { console.log(token.name); });
|
||||
|
||||
const wiki = CeL.wiki.mw_web_session;
|
||||
// wiki.page('Wikipedia:Sandbox').edit(function(page_data) { return CeL.wiki.content_of(page_data) + '\ntest'; });
|
||||
}
|
||||
</code></pre>
|
||||
(At 2021, The JavaScript parser of MediaWiki loader cannot read ECMAScript 2016 syntax.)
|
||||
|
||||
Also refer to [https://kanasimi.github.io/CeJS/_test%20suite/wikitext_parser.html the wikitext parser examples].
|
||||
|
||||
; Append parameter to template:
|
||||
<pre><code>
|
||||
const wiki = CeL.wiki.mw_web_session;
|
||||
wiki.page(mw.config.get('wgPageName')).edit(function(page_data) {
|
||||
/** {Array} parsed page content */
|
||||
const parsed = CeL.wiki.parser(page_data).parse();
|
||||
parsed.each('Template:Artwork', function(token) {
|
||||
token.push('wikidata=Q27964733');
|
||||
});
|
||||
return parsed.toString();
|
||||
}, {
|
||||
summary: 'test edit'
|
||||
}).run(function() {
|
||||
location.reload();
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
== Operation mechanism 運作機制 ==
|
||||
Main initial point: [[../wiki.js]]
|
||||
|
||||
; Essential 必要: [[../wiki.js]] → [[namespace.js]] → [[parser.js]], [[query.js]], [[page.js]], [[Flow.js]], [[list.js]], [[edit.js]], [[task.js]]
|
||||
; Optional 可選功能: [[data.js]], [[admin.js]], [[cache.js]], [[Toolforge.js]]
|
||||
; Change with wikiproject page contents 隨各 wikiproject 頁面內容變化之功能: [[template_functions.js]], [[featured_content.js]]
|
||||
|
||||
More examples: 使用範例可參照:
|
||||
<!--
|
||||
const util = require('util'); new util.promisify(CeL.wiki)(...)
|
||||
-->
|
||||
* [https://github.com/kanasimi/wikiapi JavaScript MediaWiki API for ECMAScript 2017+] / [https://github.com/kanasimi/wikiapi/blob/master/wikiapi.js wikiapi.js]
|
||||
* [https://github.com/kanasimi/wikibot Wikipedia bots demo] / [https://github.com/kanasimi/wikibot/blob/master/wiki%20loader.js wiki loader.js]
|
||||
* [[/_test suite/test.js|test.js]]
|
||||
* [https://kanasimi.github.io/CeJS/_test%20suite/wikitext_parser.html Wikitext parser examples. Wikitext 解析器使用例子]
|
||||
|
||||
|
||||
== History ==
|
||||
{| class="wikitable"
|
||||
|+ History 沿革
|
||||
! Date !! Modify
|
||||
|-
|
||||
| 2015/1/1 || Starting to write codes.
|
||||
|
||||
開始撰寫模組程式碼。
|
||||
|-
|
||||
| 2019/10/11 || 分拆至 wiki/*.js
|
||||
|-
|
||||
| 2020/5/24 || 分拆 wiki.js。基本功能僅需要 `CeL.run('application.net.wiki')`。
|
||||
|}
|
||||
|
||||
|
||||
== See also ==
|
||||
* [https://www.mediawiki.org/w/api.php MediaWiki API help]
|
902
app/node_modules/cejs/application/net/wiki/Toolforge.js
generated
vendored
Normal file
902
app/node_modules/cejs/application/net/wiki/Toolforge.js
generated
vendored
Normal file
@@ -0,0 +1,902 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科): Toolforge only functions
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* 條件合適時,應該會由 CeL.application.net.wiki 載入。
|
||||
*
|
||||
* TODO:<code>
|
||||
|
||||
|
||||
</code>
|
||||
*
|
||||
* @since 2019/10/11 拆分自 CeL.application.net.wiki
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.Toolforge',
|
||||
|
||||
require : 'data.native.|application.storage.' + '|application.net.wiki.'
|
||||
// load MediaWiki module basic functions
|
||||
+ '|application.net.wiki.namespace.',
|
||||
|
||||
// 設定不匯出的子函式。
|
||||
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;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// SQL 相關函數 @ Toolforge。
|
||||
|
||||
var
|
||||
/** {String}user home directory */
|
||||
home_directory = library_namespace.env.home,
|
||||
/** {String}Wikimedia Toolforge database host */
|
||||
TOOLSDB = 'tools-db',
|
||||
/** {String}user/bot name */
|
||||
user_name,
|
||||
/** {String}Wikimedia Toolforge name. CeL.wiki.wmflabs */
|
||||
wmflabs = wiki_API.wmflabs,
|
||||
/** {Object}Wikimedia Toolforge job data. CeL.wiki.job_data */
|
||||
job_data,
|
||||
/** node mysql handler */
|
||||
node_mysql,
|
||||
/** {Object}default SQL configurations */
|
||||
SQL_config;
|
||||
|
||||
if (home_directory
|
||||
&& (home_directory = home_directory.replace(/[\\\/]$/, '').trim())) {
|
||||
user_name = home_directory.match(/[^\\\/]+$/);
|
||||
user_name = user_name ? user_name[0] : undefined;
|
||||
if (user_name) {
|
||||
wiki_API.user_name = user_name;
|
||||
}
|
||||
// There is no CeL.storage.append_path_separator() here!
|
||||
home_directory += library_namespace.env.path_separator;
|
||||
}
|
||||
|
||||
// setup SQL config language (and database/host).
|
||||
function set_SQL_config_language(language) {
|
||||
if (!language) {
|
||||
return;
|
||||
}
|
||||
if (typeof language !== 'string') {
|
||||
library_namespace.error(
|
||||
//
|
||||
'set_SQL_config_language: Invalid language: [' + language + ']');
|
||||
return;
|
||||
}
|
||||
|
||||
if (language === TOOLSDB) {
|
||||
this.host = language;
|
||||
// delete this.database;
|
||||
return;
|
||||
}
|
||||
|
||||
// 正規化。
|
||||
var site = wiki_API.site_name(language);
|
||||
// TODO: 'zh.news'
|
||||
// 警告: this.language 可能包含 'zhwikinews' 之類。
|
||||
|
||||
this.host = site + set_SQL_config_language.hostname_postfix;
|
||||
/**
|
||||
* The database names themselves consist of the mediawiki project name,
|
||||
* suffixed with _p
|
||||
*
|
||||
* @see https://wikitech.wikimedia.org/wiki/Help:Toolforge/Database
|
||||
*/
|
||||
this.database = site + '_p';
|
||||
|
||||
// console.log(this);
|
||||
}
|
||||
|
||||
// https://wikitech.wikimedia.org/wiki/Help:Toolforge/Database#Connecting_to_the_database_replicas
|
||||
// .analytics.db.svc.wikimedia.cloud
|
||||
// @seealso https://phabricator.wikimedia.org/T142807
|
||||
set_SQL_config_language.hostname_postfix = '.web.db.svc.wikimedia.cloud';
|
||||
|
||||
/**
|
||||
* return new SQL config
|
||||
*
|
||||
* @param {String}[language]
|
||||
* database language.<br />
|
||||
* e.g., 'en', 'commons', 'wikidata', 'meta'.
|
||||
* @param {String}[user]
|
||||
* SQL database user name
|
||||
* @param {String}[password]
|
||||
* SQL database user password
|
||||
*
|
||||
* @returns {Object}SQL config
|
||||
*/
|
||||
function new_SQL_config(language, user, password) {
|
||||
var config, is_clone;
|
||||
if (user) {
|
||||
config = {
|
||||
user : user,
|
||||
password : password,
|
||||
db_prefix : user + '__',
|
||||
set_language : set_SQL_config_language
|
||||
};
|
||||
} else if (SQL_config) {
|
||||
is_clone = true;
|
||||
config = Object.clone(SQL_config);
|
||||
} else {
|
||||
config = {};
|
||||
}
|
||||
|
||||
if (typeof language === 'object') {
|
||||
if (is_clone) {
|
||||
delete config.database;
|
||||
}
|
||||
if (language.API_URL) {
|
||||
// treat language as session.
|
||||
// use set_SQL_config_language()
|
||||
config.set_language(wiki_API.site_name(language), !user);
|
||||
} else {
|
||||
Object.assign(config, language);
|
||||
}
|
||||
} else if (typeof language === 'string' && language) {
|
||||
if (is_clone) {
|
||||
delete config.database;
|
||||
}
|
||||
// change language (and database/host).
|
||||
config.set_language(language, !user);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 讀取並解析出 SQL 設定。
|
||||
*
|
||||
* @param {String}file_name
|
||||
* file name
|
||||
*
|
||||
* @returns {Object}SQL config
|
||||
*/
|
||||
function parse_SQL_config(file_name) {
|
||||
var config;
|
||||
try {
|
||||
config = library_namespace.get_file(file_name);
|
||||
} catch (e) {
|
||||
library_namespace.error(
|
||||
//
|
||||
'parse_SQL_config: Cannot read config file [ ' + file_name + ']!');
|
||||
return;
|
||||
}
|
||||
|
||||
// 應該用 parser。
|
||||
var user = config.match(/\n\s*user\s*=\s*(\S+)/), password;
|
||||
if (!user || !(password = config.match(/\n\s*password\s*=\s*(\S+)/)))
|
||||
return;
|
||||
|
||||
return new_SQL_config(wiki_API.language, user[1], password[1]);
|
||||
}
|
||||
|
||||
if (wmflabs) {
|
||||
try {
|
||||
node_mysql = require('mysql');
|
||||
if (node_mysql) {
|
||||
SQL_config = parse_SQL_config(home_directory
|
||||
// The production replicas.
|
||||
// https://wikitech.wikimedia.org/wiki/Help:Toolforge#The_databases
|
||||
// https://wikitech.wikimedia.org/wiki/Help:Toolforge/Database
|
||||
// Wikimedia Toolforge
|
||||
// 上之資料庫僅為正式上線版之刪節副本。資料並非最新版本(但誤差多於數分內),也不完全,
|
||||
// <del>甚至可能為其他 users 竄改過</del>。
|
||||
+ 'replica.my.cnf');
|
||||
}
|
||||
} catch (e) {
|
||||
library_namespace.error(e);
|
||||
}
|
||||
|
||||
if (process.env.JOB_ID && process.env.JOB_NAME) {
|
||||
// assert: process.env.ENVIRONMENT === 'BATCH'
|
||||
wiki_API.job_data = job_data = {
|
||||
id : process.env.JOB_ID,
|
||||
name : process.env.JOB_NAME,
|
||||
request : process.env.REQUEST,
|
||||
script : process.env.JOB_SCRIPT,
|
||||
stdout_file : process.env.SGE_STDOUT_PATH,
|
||||
stderr_file : process.env.SGE_STDERR_PATH,
|
||||
// 'continuous' or 'task'
|
||||
is_task : process.env.QUEUE === 'task'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* execute SQL command.
|
||||
*
|
||||
* @param {String}SQL
|
||||
* SQL command.
|
||||
* @param {Function}callback
|
||||
* 回調函數。 callback({Object}error, {Array}rows, {Array}fields)
|
||||
* @param {Object}[config]
|
||||
* configuration.
|
||||
*
|
||||
* @see https://wikitech.wikimedia.org/wiki/Help:Toolforge/Database
|
||||
*
|
||||
* @require https://github.com/mysqljs/mysql <br />
|
||||
* https://quarry.wmflabs.org/ <br />
|
||||
* TODO: https://github.com/sidorares/node-mysql2
|
||||
*/
|
||||
function run_SQL(SQL, callback, config) {
|
||||
var _callback = function(error, results, fields) {
|
||||
// the connection will return to the pool, ready to be used again by
|
||||
// someone else.
|
||||
// connection.release();
|
||||
|
||||
// close the connection and remove it from the pool
|
||||
// connection.destroy();
|
||||
|
||||
callback(error, results, fields);
|
||||
};
|
||||
_callback = callback;
|
||||
|
||||
// TypeError: Converting circular structure to JSON
|
||||
// library_namespace.debug(JSON.stringify(config), 3, 'run_SQL');
|
||||
if (!config && !(config = SQL_config)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// treat config as language.
|
||||
if (typeof config === 'string' || wiki_API.is_wiki_API(config)) {
|
||||
config = new_SQL_config(config);
|
||||
}
|
||||
|
||||
library_namespace.debug(String(SQL), 3, 'run_SQL');
|
||||
// console.log(JSON.stringify(config));
|
||||
var connection = node_mysql.createConnection(config);
|
||||
connection.connect();
|
||||
if (Array.isArray(SQL)) {
|
||||
// ("SQL", [values], callback)
|
||||
connection.query(SQL[0], SQL[1], _callback);
|
||||
} else {
|
||||
// ("SQL", callback)
|
||||
connection.query(SQL, _callback);
|
||||
}
|
||||
connection.end();
|
||||
}
|
||||
|
||||
if (false) {
|
||||
CeL.wiki.SQL('SELECT * FROM `revision` LIMIT 3000,1;',
|
||||
//
|
||||
function(error, rows, fields) {
|
||||
if (error)
|
||||
throw error;
|
||||
// console.log('The result is:');
|
||||
console.log(rows);
|
||||
});
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a new user database.
|
||||
*
|
||||
* @param {String}dbname
|
||||
* database name.
|
||||
* @param {Function}callback
|
||||
* 回調函數。
|
||||
* @param {String}[language]
|
||||
* database language.<br />
|
||||
* e.g., 'en', 'commons', 'wikidata', 'meta'.
|
||||
*
|
||||
* @see https://wikitech.wikimedia.org/wiki/Help:Tool_Labs/Database#Creating_new_databases
|
||||
*/
|
||||
function create_database(dbname, callback, language) {
|
||||
if (!SQL_config)
|
||||
return;
|
||||
|
||||
var config;
|
||||
if (typeof dbname === 'object') {
|
||||
config = Object.clone(dbname);
|
||||
dbname = config.database;
|
||||
delete config.database;
|
||||
} else {
|
||||
config = new_SQL_config(language || TOOLSDB);
|
||||
if (!language) {
|
||||
delete config.database;
|
||||
}
|
||||
}
|
||||
|
||||
library_namespace.log('create_database: Try to create database ['
|
||||
+ dbname + ']');
|
||||
if (false) {
|
||||
/**
|
||||
* 用此方法會:<br />
|
||||
* [Error: ER_PARSE_ERROR: You have an error in your SQL syntax;
|
||||
* check the manual that corresponds to your MariaDB server version
|
||||
* for the right syntax to use near ''user__db'' at line 1]
|
||||
*/
|
||||
var SQL = {
|
||||
// placeholder 佔位符
|
||||
// 避免 error.code === 'ER_DB_CREATE_EXISTS'
|
||||
sql : 'CREATE DATABASE IF NOT EXISTS ?',
|
||||
values : [ dbname ]
|
||||
};
|
||||
}
|
||||
|
||||
if (dbname.includes('`'))
|
||||
throw new Error('Invalid database name: [' + dbname + ']');
|
||||
|
||||
run_SQL('CREATE DATABASE IF NOT EXISTS `' + dbname + '`', function(
|
||||
error, rows, fields) {
|
||||
if (typeof callback !== 'function')
|
||||
return;
|
||||
if (error)
|
||||
callback(error);
|
||||
else
|
||||
callback(null, rows, fields);
|
||||
}, config);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* SQL 查詢功能之前端。
|
||||
*
|
||||
* @example <code>
|
||||
|
||||
// change language (and database/host).
|
||||
//CeL.wiki.SQL.config.set_language('en');
|
||||
CeL.wiki.SQL(SQL, function callback(error, rows, fields) { if(error) console.error(error); else console.log(rows); }, 'en');
|
||||
|
||||
// get sitelink count of wikidata items
|
||||
// https://www.mediawiki.org/wiki/Wikibase/Schema/wb_items_per_site
|
||||
// https://www.wikidata.org/w/api.php?action=help&modules=wbsetsitelink
|
||||
var SQL_get_sitelink_count = 'SELECT ips_item_id, COUNT(*) AS `link_count` FROM wb_items_per_site GROUP BY ips_item_id LIMIT 10';
|
||||
var SQL_session = new CeL.wiki.SQL(function(error){}, 'wikidata');
|
||||
function callback(error, rows, fields) { if(error) console.error(error); else console.log(rows); SQL_session.connection.destroy(); }
|
||||
SQL_session.SQL(SQL_get_sitelink_count, callback);
|
||||
|
||||
// one-time method
|
||||
CeL.wiki.SQL(SQL_get_sitelink_count, callback, 'wikidata');
|
||||
|
||||
* </code>
|
||||
*
|
||||
* @example <code>
|
||||
|
||||
// 進入 default host (TOOLSDB)。
|
||||
var SQL_session = new CeL.wiki.SQL(()=>{});
|
||||
// 進入 default host (TOOLSDB),並預先創建 user's database 'dbname' (e.g., 's00000__dbname')
|
||||
var SQL_session = new CeL.wiki.SQL('dbname', ()=>{});
|
||||
// 進入 zhwiki.zhwiki_p。
|
||||
var SQL_session = new CeL.wiki.SQL(()=>{}, 'zh');
|
||||
// 進入 zhwiki.zhwiki_p,並預先創建 user's database 'dbname' (e.g., 's00000__dbname')
|
||||
var SQL_session = new CeL.wiki.SQL('dbname', ()=>{}, 'zh');
|
||||
|
||||
// create {SQL_session}instance
|
||||
new CeL.wiki.SQL('mydb', function callback(error, rows, fields) { if(error) console.error(error); } )
|
||||
// run SQL query
|
||||
.SQL(SQL, function callback(error, rows, fields) { if(error) console.error(error); } );
|
||||
|
||||
SQL_session.connection.destroy();
|
||||
|
||||
* </code>
|
||||
*
|
||||
* @param {String}[dbname]
|
||||
* database name.
|
||||
* @param {Function}callback
|
||||
* 回調函數。 callback(error)
|
||||
* @param {String}[language]
|
||||
* database language (and database/host). default host: TOOLSDB.<br />
|
||||
* e.g., 'en', 'commons', 'wikidata', 'meta'.
|
||||
*
|
||||
* @returns {SQL_session}instance
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SQL_session(dbname, callback, language) {
|
||||
if (!(this instanceof SQL_session)) {
|
||||
if (typeof language === 'object') {
|
||||
language = new_SQL_config(language);
|
||||
} else if (typeof language === 'string' && language) {
|
||||
// change language (and database/host).
|
||||
SQL_config.set_language(language);
|
||||
if (language === TOOLSDB)
|
||||
delete SQL_config.database;
|
||||
language = null;
|
||||
}
|
||||
// dbname as SQL query string.
|
||||
return run_SQL(dbname, callback, language);
|
||||
}
|
||||
|
||||
if (typeof dbname === 'function' && !language) {
|
||||
// shift arguments
|
||||
language = callback;
|
||||
callback = dbname;
|
||||
dbname = null;
|
||||
}
|
||||
|
||||
this.config = new_SQL_config(language || TOOLSDB);
|
||||
if (dbname) {
|
||||
if (typeof dbname === 'object') {
|
||||
Object.assign(this.config, dbname);
|
||||
} else {
|
||||
// 自動添加 prefix。
|
||||
this.config.database = this.config.db_prefix + dbname;
|
||||
}
|
||||
} else if (this.config.host === TOOLSDB) {
|
||||
delete this.config.database;
|
||||
} else {
|
||||
// this.config.database 已經在 set_SQL_config_language() 設定。
|
||||
}
|
||||
|
||||
var _this = this;
|
||||
this.connect(function(error) {
|
||||
// console.error(error);
|
||||
if (error && error.code === 'ER_BAD_DB_ERROR'
|
||||
&& !_this.config.no_create && _this.config.database) {
|
||||
// Error: ER_BAD_DB_ERROR: Unknown database '...'
|
||||
create_database(_this.config, callback);
|
||||
} else if (typeof callback === 'function') {
|
||||
callback(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// need reset connection,
|
||||
function need_reconnect(error) {
|
||||
return error
|
||||
// Error: Cannot enqueue Handshake after fatal error.
|
||||
&& (error.code === 'PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR'
|
||||
// ECONNRESET: socket hang up
|
||||
|| error.code === 'ECONNRESET');
|
||||
}
|
||||
|
||||
// run SQL query
|
||||
SQL_session.prototype.SQL = function(SQL, callback) {
|
||||
var _this = this;
|
||||
this.connection.query(SQL, function(error) {
|
||||
if (need_reconnect(error)) {
|
||||
// re-connect. 可能已經斷線。
|
||||
_this.connection.connect(function(error) {
|
||||
if (error) {
|
||||
// console.error(error);
|
||||
}
|
||||
_this.connection.query(SQL, callback);
|
||||
});
|
||||
} else {
|
||||
callback.apply(null, arguments);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
SQL_session.prototype.connect = function(callback, force) {
|
||||
if (!force)
|
||||
try {
|
||||
var _this = this;
|
||||
this.connection.connect(function(error) {
|
||||
if (need_reconnect(error)) {
|
||||
// re-connect.
|
||||
_this.connect(callback, true);
|
||||
} else if (typeof callback === 'function')
|
||||
callback(error);
|
||||
});
|
||||
return this;
|
||||
} catch (e) {
|
||||
// TODO: handle exception
|
||||
}
|
||||
|
||||
try {
|
||||
this.connection.end();
|
||||
} catch (e) {
|
||||
// TODO: handle exception
|
||||
}
|
||||
// 需要重新設定 this.connection,否則會出現:
|
||||
// Error: Cannot enqueue Handshake after invoking quit.
|
||||
this.connection = node_mysql.createConnection(this.config);
|
||||
this.connection.connect(callback);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* get database list.
|
||||
*
|
||||
* <code>
|
||||
|
||||
var SQL_session = new CeL.wiki.SQL('testdb',
|
||||
//
|
||||
function callback(error, rows, fields) {
|
||||
if (error)
|
||||
console.error(error);
|
||||
else
|
||||
s.databases(function(list) {
|
||||
console.log(list);
|
||||
});
|
||||
});
|
||||
|
||||
</code>
|
||||
*
|
||||
* @param {Function}callback
|
||||
* 回調函數。
|
||||
* @param {Boolean}all
|
||||
* get all databases. else: get my databases.
|
||||
*
|
||||
* @returns {SQL_session}
|
||||
*/
|
||||
SQL_session.prototype.databases = function(callback, all) {
|
||||
var _this = this;
|
||||
function filter(dbname) {
|
||||
return dbname.startsWith(_this.config.db_prefix);
|
||||
}
|
||||
|
||||
if (this.database_cache) {
|
||||
var list = this.database_cache;
|
||||
if (!all)
|
||||
// .filter() 會失去 array 之其他屬性。
|
||||
list = list.filter(filter);
|
||||
if (typeof callback === 'function')
|
||||
callback(list);
|
||||
return this;
|
||||
}
|
||||
|
||||
var SQL = 'SHOW DATABASES';
|
||||
if (false && !all)
|
||||
// SHOW DATABASES LIKE 'pattern';
|
||||
SQL += " LIKE '" + this.config.db_prefix + "%'";
|
||||
|
||||
this.connect(function(error) {
|
||||
// reset connection,
|
||||
// 預防 PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR
|
||||
_this.connection.query(SQL, function(error, rows, fields) {
|
||||
if (error || !Array.isArray(rows)) {
|
||||
library_namespace.error(error);
|
||||
rows = null;
|
||||
} else {
|
||||
rows = rows.map(function(row) {
|
||||
for ( var field in row)
|
||||
return row[field];
|
||||
});
|
||||
_this.database_cache = rows;
|
||||
if (!all)
|
||||
// .filter() 會失去 array 之其他屬性。
|
||||
rows = rows.filter(filter);
|
||||
// console.log(rows);
|
||||
}
|
||||
if (typeof callback === 'function')
|
||||
callback(rows);
|
||||
});
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
if (SQL_config) {
|
||||
library_namespace
|
||||
.debug('wiki_API.SQL_session: You may use SQL to get data.');
|
||||
wiki_API.SQL = SQL_session;
|
||||
// export 導出: CeL.wiki.SQL() 僅可在 Wikimedia Toolforge 上使用。
|
||||
wiki_API.SQL.config = SQL_config;
|
||||
// wiki_API.SQL.create = create_database;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
/**
|
||||
* Convert MediaWiki database timestamp to ISO 8601 format.<br />
|
||||
* UTC: 'yyyymmddhhmmss' → 'yyyy-mm-ddThh:mm:ss'
|
||||
*
|
||||
* @param {String|Buffer}timestamp
|
||||
* MediaWiki database timestamp
|
||||
*
|
||||
* @returns {String}ISO 8601 Data elements and interchange formats
|
||||
*
|
||||
* @see https://www.mediawiki.org/wiki/Manual:Timestamp
|
||||
*/
|
||||
function SQL_timestamp_to_ISO(timestamp) {
|
||||
if (!timestamp) {
|
||||
// ''?
|
||||
return;
|
||||
}
|
||||
// timestamp可能為{Buffer}
|
||||
timestamp = timestamp.toString('utf8').chunk(2);
|
||||
if (timestamp.length !== 7) {
|
||||
// 'NULL'?
|
||||
return;
|
||||
}
|
||||
|
||||
return timestamp[0] + timestamp[1]
|
||||
//
|
||||
+ '-' + timestamp[2] + '-' + timestamp[3]
|
||||
//
|
||||
+ 'T' + timestamp[4] + ':' + timestamp[5] + ':' + timestamp[6] + 'Z';
|
||||
}
|
||||
|
||||
function generate_SQL_WHERE(condition, field_prefix) {
|
||||
var condition_array = [], value_array = [];
|
||||
|
||||
if (typeof condition === 'string') {
|
||||
;
|
||||
|
||||
} else if (Array.isArray(condition)) {
|
||||
// TODO: for ' OR '
|
||||
condition = condition.join(' AND ');
|
||||
|
||||
} else if (library_namespace.is_Object(condition)) {
|
||||
for ( var name in condition) {
|
||||
var value = condition[name];
|
||||
if (value === undefined) {
|
||||
// 跳過這一筆設定。
|
||||
continue;
|
||||
}
|
||||
if (!name) {
|
||||
// condition[''] = [ condition 1, condition 2, ...];
|
||||
if (Array.isArray(value)) {
|
||||
value_array.append(value);
|
||||
} else {
|
||||
value_array.push(value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!/^[a-z_]+$/.test(name)) {
|
||||
throw 'Invalid field name: ' + name;
|
||||
}
|
||||
if (!name.startsWith(field_prefix)) {
|
||||
name = field_prefix + name;
|
||||
}
|
||||
var matched = typeof value === 'string'
|
||||
// TODO: for other operators
|
||||
// @see https://mariadb.com/kb/en/mariadb/select/
|
||||
// https://mariadb.com/kb/en/mariadb/functions-and-operators/
|
||||
&& value.match(/^([<>!]?=|[<>]|<=>|IN |IS )([\s\S]+)$/);
|
||||
if (matched) {
|
||||
name += matched[1] + '?';
|
||||
// DO NOT quote the value yourself!!
|
||||
value = matched[2];
|
||||
// Number.MAX_SAFE_INTEGER starts from 9.
|
||||
if (/^[+\-]?[1-9]\d{0,15}$/.test(value)
|
||||
// ↑ 15 = String(Number.MAX_SAFE_INTEGER).length-1
|
||||
&& +value <= Number.MAX_SAFE_INTEGER) {
|
||||
value = +value;
|
||||
}
|
||||
} else {
|
||||
name += '=?';
|
||||
}
|
||||
condition_array.push(name);
|
||||
value_array.push(value);
|
||||
}
|
||||
|
||||
// TODO: for ' OR '
|
||||
condition = condition_array.join(' AND ');
|
||||
|
||||
} else {
|
||||
library_namespace.error('Invalid condition: '
|
||||
+ JSON.stringify(condition));
|
||||
return;
|
||||
}
|
||||
|
||||
return [ condition ? ' WHERE ' + condition : '', value_array ];
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
// https://www.mediawiki.org/wiki/API:RecentChanges
|
||||
// const
|
||||
var ENUM_rc_type = 'edit,new,move,log,move over redirect,external,categorize';
|
||||
|
||||
/**
|
||||
* Get page title 頁面標題 list of [[Special:RecentChanges]] 最近更改.
|
||||
*
|
||||
* @examples<code>
|
||||
|
||||
// get title list
|
||||
CeL.wiki.recent(function(rows){console.log(rows.map(function(row){return row.title;}));}, {language:'ja', namespace:0, limit:20});
|
||||
|
||||
// 應並用 timestamp + this_oldid
|
||||
CeL.wiki.recent(function(rows){console.log(rows.map(function(row){return [row.title,row.rev_id,row.row.rc_timestamp.toString()];}));}, {where:{timestamp:'>=20170327143435',this_oldid:'>'+43772537}});
|
||||
|
||||
</code>
|
||||
*
|
||||
* TODO: filter
|
||||
*
|
||||
* @param {Function}callback
|
||||
* 回調函數。 callback({Array}page title 頁面標題 list)
|
||||
* @param {Object}[options]
|
||||
* 附加參數/設定選擇性/特殊功能與選項。
|
||||
*
|
||||
* @see https://www.mediawiki.org/wiki/Manual:Recentchanges_table
|
||||
* https://www.mediawiki.org/wiki/Actor_migration
|
||||
*/
|
||||
function get_recent_via_databases(callback, options) {
|
||||
if (options && (typeof options === 'string')) {
|
||||
options = {
|
||||
// treat options as language
|
||||
language : options
|
||||
};
|
||||
} else {
|
||||
options = library_namespace.setup_options(options);
|
||||
}
|
||||
// console.trace(options);
|
||||
|
||||
var SQL = options.SQL;
|
||||
if (!SQL) {
|
||||
SQL = Object.create(null);
|
||||
if (options.bot === 0 || options.bot === 1) {
|
||||
// assert: 0 || 1
|
||||
SQL.bot = options.bot;
|
||||
}
|
||||
// 不指定namespace,或者指定namespace為((undefined)): 取得所有的namespace。
|
||||
/** {Integer|String}namespace NO. */
|
||||
var namespace = wiki_API.namespace(options.namespace);
|
||||
if (namespace !== undefined) {
|
||||
SQL.namespace = namespace;
|
||||
}
|
||||
Object.assign(SQL,
|
||||
// {String|Array|Object}options.where: 自訂篩選條件。
|
||||
options.where);
|
||||
SQL = generate_SQL_WHERE(SQL, 'rc_');
|
||||
// console.log(SQL);
|
||||
|
||||
// https://phabricator.wikimedia.org/T223406
|
||||
// TODO: 舊版上 `actor`, `comment` 這兩個資料表不存在會出錯,需要先偵測。
|
||||
// TODO: use JSON: https://phabricator.wikimedia.org/T299417
|
||||
var fields = [
|
||||
'*',
|
||||
// https://www.mediawiki.org/wiki/Manual:Actor_table#actor_id
|
||||
'(SELECT `actor_user` FROM `actor` WHERE `actor`.`actor_id` = `recentchanges`.`rc_actor`) AS `userid`',
|
||||
'(SELECT `actor_name` FROM `actor` WHERE `actor`.`actor_id` = `recentchanges`.`rc_actor`) AS `user_name`',
|
||||
// https://www.mediawiki.org/wiki/Manual:Comment_table#comment_id
|
||||
'(SELECT `comment_text` FROM `comment` WHERE `comment`.`comment_id` = `recentchanges`.`rc_comment_id`) AS `comment`',
|
||||
'(SELECT `comment_data` FROM `comment` WHERE `comment`.`comment_id` = `recentchanges`.`rc_comment_id`) AS `comment_data`' ];
|
||||
|
||||
SQL[0] = 'SELECT ' + fields.join(',')
|
||||
// https://www.mediawiki.org/wiki/Manual:Recentchanges_table
|
||||
+ ' FROM `recentchanges`' + SQL[0]
|
||||
// new → old, may contain duplicate title.
|
||||
// or `rc_timestamp`
|
||||
// or rc_this_oldid, but too slow (no index).
|
||||
// ASC: 小 → 大,DESC: 大 → 小
|
||||
+ ' ORDER BY `rc_this_oldid` ASC LIMIT ' + (
|
||||
/** {ℕ⁰:Natural+0}limit count. */
|
||||
options.limit > 0 ? Math.min(options.limit
|
||||
// 筆數限制。就算隨意輸入,強制最多只能這麼多筆資料。
|
||||
, 1e4)
|
||||
// default records to get
|
||||
: options.where ? 1e4 : 5000);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
console.log([ options.config, options.language,
|
||||
options[KEY_SESSION] && options[KEY_SESSION].language ]);
|
||||
console.log(options[KEY_SESSION]);
|
||||
throw 1;
|
||||
}
|
||||
|
||||
run_SQL(SQL, function(error, rows, fields) {
|
||||
if (error) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var result = [];
|
||||
rows.forEach(function(row) {
|
||||
if (!(row.rc_user > 0) && !(row.rc_type < 5)
|
||||
//
|
||||
&& (!('rc_type' in options)
|
||||
//
|
||||
|| options.rc_type !== ENUM_rc_type[row.rc_type])) {
|
||||
// On wikis using Wikibase the results will otherwise be
|
||||
// meaningless.
|
||||
return;
|
||||
}
|
||||
|
||||
var namespace_text = row.rc_namespace
|
||||
// pass session = options[KEY_SESSION]
|
||||
? wiki_API.namespace.name_of(row.rc_namespace, options) + ':'
|
||||
: '';
|
||||
// 基本上 API 盡可能模擬 recentchanges,與之一致。
|
||||
result.push({
|
||||
type : ENUM_rc_type[row.rc_type],
|
||||
// namespace
|
||||
ns : row.rc_namespace,
|
||||
// .rc_title 未加上 namespace prefix!
|
||||
title : (namespace_text
|
||||
// @see normalize_page_name()
|
||||
+ row.rc_title.toString()).replace(/_/g, ' '),
|
||||
// links to the page_id key in the page table
|
||||
// 0: 可能為flow. 此時title為主頁面名,非topic。由.rc_params可獲得相關資訊。
|
||||
pageid : row.rc_cur_id,
|
||||
// rev_id
|
||||
// Links to the rev_id key of the new page revision
|
||||
// (after the edit occurs) in the revision table.
|
||||
revid : row.rc_this_oldid,
|
||||
old_revid : row.rc_last_oldid,
|
||||
rcid : row.rc_id,
|
||||
user : row.user_name && row.user_name.toString()
|
||||
// text of the username for the user that made the
|
||||
// change, or the IP address if the change was made by
|
||||
// an unregistered user. Corresponds to rev_user_text
|
||||
//
|
||||
// `rc_user_text` deprecated: MediaWiki version: ≤ 1.33
|
||||
|| row.rc_user_text && row.rc_user_text.toString(),
|
||||
// NULL for anonymous edits
|
||||
userid : row.userid
|
||||
// 0 for anonymous edits
|
||||
// `rc_user` deprecated: MediaWiki version: ≤ 1.33
|
||||
|| row.rc_user,
|
||||
// old_length
|
||||
oldlen : row.rc_old_len,
|
||||
// new length
|
||||
newlen : row.rc_new_len,
|
||||
// Corresponds to rev_timestamp
|
||||
// use new Date(.timestamp)
|
||||
timestamp : SQL_timestamp_to_ISO(row.rc_timestamp),
|
||||
comment : row.comment && row.comment.toString()
|
||||
// `rc_comment` deprecated: MediaWiki version: ≤ 1.32
|
||||
|| row.rc_comment && row.rc_comment.toString(),
|
||||
// usually NULL
|
||||
comment_data : row.comment_data
|
||||
&& row.comment_data.toString(),
|
||||
// parsedcomment : TODO,
|
||||
logid : row.rc_logid,
|
||||
// TODO
|
||||
logtype : row.rc_log_type,
|
||||
logaction : row.rc_log_action.toString(),
|
||||
// logparams: TODO: should be {Object}, e.g., {userid:0}
|
||||
logparams : row.rc_params.toString(),
|
||||
// tags: ["TODO"],
|
||||
|
||||
// 以下為recentchanges之外,本函數額外加入。
|
||||
is_new : !!row.rc_new,
|
||||
// e.g., 1 or 0
|
||||
// is_bot : !!row.rc_bot,
|
||||
// is_minor : !!row.rc_minor,
|
||||
// e.g., mw.edit
|
||||
is_Flow : row.rc_source.toString() === 'flow',
|
||||
// patrolled : !!row.rc_patrolled,
|
||||
// deleted : !!row.rc_deleted,
|
||||
|
||||
row : row
|
||||
});
|
||||
});
|
||||
callback(result);
|
||||
},
|
||||
// SQL config
|
||||
options.config || options.language || options[KEY_SESSION]);
|
||||
}
|
||||
|
||||
// 可能會因環境而不同的功能。讓 wiki_API.recent 採用較有效率的實現方式。
|
||||
if (SQL_config) {
|
||||
wiki_API.recent =
|
||||
// SQL_config ? get_recent_via_databases : get_recent_via_API;
|
||||
get_recent_via_databases;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
// @inner
|
||||
library_namespace.set_method(wiki_API, {
|
||||
SQL_config : SQL_config,
|
||||
new_SQL_config : new_SQL_config,
|
||||
run_SQL : run_SQL
|
||||
});
|
||||
|
||||
// 不設定(hook)本 module 之 namespace,僅執行 module code。
|
||||
return library_namespace.env.not_to_extend_keyword;
|
||||
}
|
372
app/node_modules/cejs/application/net/wiki/admin.js
generated
vendored
Normal file
372
app/node_modules/cejs/application/net/wiki/admin.js
generated
vendored
Normal file
@@ -0,0 +1,372 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科): 管理員相關的 adminship
|
||||
* functions
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* TODO:<code>
|
||||
|
||||
|
||||
</code>
|
||||
*
|
||||
* @since 2019/10/12 拆分自 CeL.application.net.wiki
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.admin',
|
||||
|
||||
require : 'application.net.wiki.'
|
||||
// load MediaWiki module basic functions
|
||||
+ '|application.net.wiki.namespace.'
|
||||
//
|
||||
+ '|application.net.wiki.query.|application.net.wiki.page.',
|
||||
|
||||
// 設定不匯出的子函式。
|
||||
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;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// administrator functions. 管理員相關函數。
|
||||
|
||||
// 自 options 汲取出 parameters。
|
||||
// TODO: 整合進 normalize_parameters。
|
||||
// default_parameters[parameter name] = required
|
||||
function draw_parameters(options, default_parameters, token_type) {
|
||||
if (!options) {
|
||||
// Invalid options/parameters
|
||||
return 'No options specified';
|
||||
}
|
||||
|
||||
// 汲取出 parameters。
|
||||
var parameters = Object.create(null);
|
||||
if (default_parameters) {
|
||||
for ( var parameter_name in default_parameters) {
|
||||
if (parameter_name in options) {
|
||||
// in case options[parameter_name] === false
|
||||
if (options[parameter_name])
|
||||
parameters[parameter_name] = options[parameter_name];
|
||||
} else if (default_parameters[parameter_name]) {
|
||||
// 表示此屬性為必須存在/指定的屬性。
|
||||
// This parameter is required.
|
||||
return 'No property ' + parameter_name + ' specified';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var session = options[KEY_SESSION];
|
||||
|
||||
// assert: 有parameters, e.g., {Object}parameters
|
||||
// 可能沒有 session
|
||||
|
||||
// ----------------------------
|
||||
// 處理 target page。
|
||||
var default_KEY_ID = 'pageid', default_KEY_TITLE = 'title', KEY_ID = default_KEY_ID, KEY_TITLE = default_KEY_TITLE;
|
||||
if (parameters.to) {
|
||||
// move_to
|
||||
KEY_ID = 'fromid';
|
||||
KEY_TITLE = 'from';
|
||||
}
|
||||
|
||||
// 都先從 options 取值,再從 session 取值。
|
||||
if (options[KEY_ID] >= 0 || options[default_KEY_ID] >= 0) {
|
||||
parameters[KEY_ID] = options[KEY_ID] >= 0 ? options[KEY_ID]
|
||||
: options[default_KEY_ID];
|
||||
} else if (options[KEY_TITLE] || options[default_KEY_TITLE]) {
|
||||
parameters[KEY_TITLE] = wiki_API.title_of(options[KEY_TITLE]
|
||||
// options.from_title
|
||||
|| options[default_KEY_TITLE]);
|
||||
} else if (wiki_API.is_page_data(session && session.last_page)) {
|
||||
// options.page_data
|
||||
if (session.last_page.pageid >= 0)
|
||||
parameters[KEY_ID] = session.last_page.pageid;
|
||||
else
|
||||
parameters[KEY_TITLE] = session.last_page.title;
|
||||
} else {
|
||||
// 可能沒有 page_data
|
||||
if (library_namespace.is_debug()) {
|
||||
library_namespace.error('draw_parameters: No page specified: '
|
||||
+ JSON.stringify(options));
|
||||
}
|
||||
return 'No page id/title specified';
|
||||
}
|
||||
|
||||
// ----------------------------
|
||||
// 處理 token。
|
||||
if (!token_type) {
|
||||
token_type = 'csrf';
|
||||
}
|
||||
var token = options.token || session && session.token;
|
||||
if (token && typeof token === 'object') {
|
||||
// session.token.csrftoken
|
||||
token = token[token_type + 'token'];
|
||||
}
|
||||
if (!token) {
|
||||
// TODO: use session
|
||||
if (false) {
|
||||
library_namespace.error('draw_parameters: No token specified: '
|
||||
+ options);
|
||||
}
|
||||
return 'No ' + token_type + 'token specified';
|
||||
}
|
||||
parameters.token = token;
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
// use "csrf" token retrieved from action=query&meta=tokens
|
||||
// callback(response, error);
|
||||
function wiki_operator(action, default_parameters, options, callback) {
|
||||
// default_parameters
|
||||
// Warning: 除 pageid/title/token 之外,這邊只要是能指定給 API 的,皆必須列入!
|
||||
var parameters = draw_parameters(options, default_parameters);
|
||||
// console.log(parameters);
|
||||
if (!library_namespace.is_Object(parameters)) {
|
||||
// error occurred.
|
||||
if (typeof callback === 'function')
|
||||
callback(undefined, parameters);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: 若是頁面不存在/已刪除,那就直接跳出。
|
||||
|
||||
if (action === 'move') {
|
||||
library_namespace.is_debug((parameters.fromid || parameters.from)
|
||||
// .move_to_title
|
||||
+ ' → ' + parameters.to, 1, 'wiki_operator.move');
|
||||
}
|
||||
|
||||
wiki_API.query({
|
||||
action : action
|
||||
}, function(response, error) {
|
||||
// console.log(JSON.stringify(response));
|
||||
if (wiki_API.query.handle_error(response, error, callback)) {
|
||||
return;
|
||||
}
|
||||
callback(response[action]);
|
||||
}, parameters, options);
|
||||
}
|
||||
|
||||
// ================================================================================================================
|
||||
|
||||
// wiki_API.delete(): remove / delete a page.
|
||||
wiki_API['delete'] = function(options, callback) {
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=delete
|
||||
|
||||
/**
|
||||
* response: <code>
|
||||
{"delete":{"title":"Title","reason":"content was: \"...\", and the only contributor was \"[[Special:Contributions/Cewbot|Cewbot]]\" ([[User talk:Cewbot|talk]])","logid":0000}}
|
||||
{"error":{"code":"nosuchpageid","info":"There is no page with ID 0.","*":"See https://test.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce> for notice of API deprecations and breaking changes."},"servedby":"mw1232"}
|
||||
* </code>
|
||||
*/
|
||||
|
||||
wiki_operator('delete', {
|
||||
reason : false,
|
||||
tags : false,
|
||||
watchlist : false,
|
||||
watchlistexpiry : false,
|
||||
oldimage : false
|
||||
}, options, callback);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
// wiki_API.move_to(): move a page from `from` to target `to`.
|
||||
wiki_API.move_to = function(options, callback) {
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=move
|
||||
var default_parameters = {
|
||||
to : true,
|
||||
reason : false,
|
||||
movetalk : false,
|
||||
movesubpages : false,
|
||||
noredirect : false,
|
||||
watchlist : false,
|
||||
ignorewarnings : false,
|
||||
tags : false
|
||||
};
|
||||
|
||||
if (!options || !options.reason) {
|
||||
library_namespace
|
||||
.warn('wiki_API.move_to: Should set reason when moving page!');
|
||||
}
|
||||
|
||||
/**
|
||||
* response: <code>
|
||||
{"error":{"code":"nosuchpageid","info":"There is no page with ID 0.","*":"See https://zh.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce> for notice of API deprecations and breaking changes."},"servedby":"mw1277"}
|
||||
error:
|
||||
{"code":"articleexists","info":"A page of that name already exists, or the name you have chosen is not valid. Please choose another name.","*":"See https://zh.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce> for notice of API deprecations and breaking changes."}
|
||||
{"code":"selfmove","info":"The title is the same; cannot move a page over itself.","*":"See https://zh.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce> for notice of API deprecations and breaking changes."}
|
||||
|
||||
{ from: 'from', to: 'to', reason: '', redirectcreated: '', moveoverredirect: '' }
|
||||
{ error: { code: 'articleexists', info: 'A page already exists at [[:To]], or the page name you have chosen is not valid. Please choose another name.', '*': 'See https://test.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce> for notice of API deprecations and breaking changes.' } }
|
||||
|
||||
</code>
|
||||
*/
|
||||
|
||||
// console.log(options);
|
||||
wiki_operator('move', default_parameters, options, callback);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
// @see wiki_API.is_protected
|
||||
// Change the protection level of a page.
|
||||
wiki_API.protect = function(options, callback) {
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=protect
|
||||
|
||||
/**
|
||||
* response: <code>
|
||||
{"protect":{"title":"title","reason":"存檔保護作業","protections":[{"edit":"sysop","expiry":"infinite"},{"move":"sysop","expiry":"infinite"}]}}
|
||||
{"servedby":"mw1203","error":{"code":"nosuchpageid","info":"There is no page with ID 2006","*":"See https://zh.wikinews.org/w/api.php for API usage"}}
|
||||
* </code>
|
||||
*/
|
||||
|
||||
wiki_operator('protect', {
|
||||
// protections: e.g., 'edit=sysop|move=sysop', 一般說來edit應與move同步。
|
||||
protections : true,
|
||||
// 在正式場合,最好給個好的理由。
|
||||
// reason: @see [[MediaWiki:Protect-dropdown]]
|
||||
reason : false,
|
||||
// expiry : 'infinite',
|
||||
expiry : false,
|
||||
tags : false,
|
||||
cascade : false,
|
||||
watchlist : false
|
||||
}, Object.assign({
|
||||
protections : 'edit=sysop|move=sysop'
|
||||
}, options), callback);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
// rollback 僅能快速撤銷/回退/還原某一頁面最新版本之作者(最後一位使用者)一系列所有編輯至另一作者的編輯
|
||||
// The rollback revision will be marked as minor.
|
||||
wiki_API.rollback = function(options, callback) {
|
||||
var session = wiki_API.session_of_options(options);
|
||||
|
||||
if (session && !session.token.rollbacktoken) {
|
||||
session.get_token(function() {
|
||||
wiki_API.rollback(options, callback);
|
||||
}, 'rollback');
|
||||
}
|
||||
|
||||
var parameters = draw_parameters(options, {
|
||||
// default_parameters
|
||||
// Warning: 除外pageid/title/token這邊只要是能指定給 API 的,皆必須列入!
|
||||
user : false,
|
||||
summary : false,
|
||||
markbot : false,
|
||||
tags : false
|
||||
}, 'rollback');
|
||||
if (!library_namespace.is_Object(parameters)) {
|
||||
// error occurred.
|
||||
if (typeof callback === 'function')
|
||||
callback(undefined, parameters);
|
||||
return;
|
||||
}
|
||||
|
||||
// 都先從 options 取值,再從 session 取值。
|
||||
var page_data =
|
||||
// options.page_data ||
|
||||
options.pageid && options || session && session.last_page;
|
||||
|
||||
// assert: 有parameters, e.g., {Object}parameters
|
||||
// 可能沒有 session, page_data
|
||||
|
||||
if (!parameters.user && wiki_API.content_of.revision(page_data)) {
|
||||
// 將最後一個版本的編輯者當作回退對象。
|
||||
parameters.user = wiki_API.content_of.revision(page_data).user;
|
||||
}
|
||||
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=rollback
|
||||
// If the last user who edited the page made multiple edits in a row,
|
||||
// they will all be rolled back.
|
||||
if (!parameters.user) {
|
||||
// 抓最後的編輯者試試。
|
||||
// 要用pageid的話,得採page_data,就必須保證兩者相同。
|
||||
if (!parameters.title && page_data
|
||||
&& parameters.pageid !== page_data.pageid) {
|
||||
callback(undefined, 'parameters.pageid !== page_data.pageid');
|
||||
return;
|
||||
}
|
||||
wiki_API.page(page_data || parameters.title, function(page_data,
|
||||
error) {
|
||||
if (error || !wiki_API.content_of.revision(page_data)
|
||||
// 保證不會再持續執行。
|
||||
|| !wiki_API.content_of.revision(page_data).user) {
|
||||
if (false) {
|
||||
library_namespace.error(
|
||||
//
|
||||
'wiki_API.rollback: No user name specified!');
|
||||
}
|
||||
|
||||
callback(undefined,
|
||||
//
|
||||
'No user name specified and I cannot guess it!');
|
||||
return;
|
||||
}
|
||||
wiki_API.rollback(options, callback);
|
||||
}, Object.assign({
|
||||
rvprop : 'ids|timestamp|user'
|
||||
}, options));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!('markbot' in parameters) && options.bot) {
|
||||
parameters.markbot = options.bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* response: <code>
|
||||
{"rollback":{"title":"title","pageid":1,"summary":"","revid":9,"old_revid":7,"last_revid":1,"messageHtml":"<p></p>"}}
|
||||
{"servedby":"mw1190","error":{"code":"badtoken","info":"Invalid token","*":"See https://zh.wikinews.org/w/api.php for API usage"}}
|
||||
* </code>
|
||||
*/
|
||||
wiki_API.query({
|
||||
action : 'rollback'
|
||||
}, function(response) {
|
||||
var error = response && response.error;
|
||||
if (error) {
|
||||
callback(response, error);
|
||||
} else {
|
||||
// revid 回滾的版本ID。
|
||||
// old_revid 被回滾的第一個(最新)修訂的修訂ID。
|
||||
// last_revid 被回滾最後一個(最舊)版本的修訂ID。
|
||||
// 如果回滾不會改變的頁面,沒有新修訂而成。在這種情況下,revid將等於old_revid。
|
||||
callback(response.rollback);
|
||||
}
|
||||
}, parameters, options);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
// 目前的修訂,不可隱藏。
|
||||
// This is the current revision. It cannot be hidden.
|
||||
wiki_API.hide = function(options, callback) {
|
||||
TODO;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
// 不設定(hook)本 module 之 namespace,僅執行 module code。
|
||||
return library_namespace.env.not_to_extend_keyword;
|
||||
}
|
812
app/node_modules/cejs/application/net/wiki/cache.js
generated
vendored
Normal file
812
app/node_modules/cejs/application/net/wiki/cache.js
generated
vendored
Normal file
@@ -0,0 +1,812 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科): cache
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* TODO:<code>
|
||||
|
||||
|
||||
</code>
|
||||
*
|
||||
* @since 2020/5/24 6:21:13 拆分自 CeL.application.net.wiki
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.cache',
|
||||
|
||||
require : 'data.native.'
|
||||
// for library_namespace.get_URL
|
||||
+ '|application.net.Ajax.' + '|application.net.wiki.'
|
||||
// load MediaWiki module basic functions
|
||||
+ '|application.net.wiki.namespace.',
|
||||
|
||||
// 設定不匯出的子函式。
|
||||
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;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/** {Object|Function}fs in node.js */
|
||||
var node_fs;
|
||||
try {
|
||||
if (library_namespace.platform.nodejs)
|
||||
// @see https://nodejs.org/api/fs.html
|
||||
node_fs = require('fs');
|
||||
if (typeof node_fs.readFile !== 'function')
|
||||
throw true;
|
||||
} catch (e) {
|
||||
// enumerate for wiki_API.cache
|
||||
// 模擬 node.js 之 fs,以達成最起碼的效果(即無 cache 功能的情況)。
|
||||
library_namespace.warn(this.id
|
||||
+ ': 無 node.js 之 fs,因此不具備 cache 或 SQL 功能。');
|
||||
node_fs = {
|
||||
// library_namespace.storage.read_file()
|
||||
readFile : function(file_path, options, callback) {
|
||||
library_namespace.error('Cannot read file ' + file_path);
|
||||
if (typeof callback === 'function')
|
||||
callback(true);
|
||||
},
|
||||
// library_namespace.storage.write_file()
|
||||
writeFile : function(file_path, data, options, callback) {
|
||||
library_namespace.error('Cannot write to file ' + file_path);
|
||||
if (typeof options === 'function' && !callback)
|
||||
callback = options;
|
||||
if (typeof callback === 'function')
|
||||
callback(true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* cache 相關函數:
|
||||
*
|
||||
* @see application.storage.file.get_cache_file
|
||||
* application.OS.Windows.file.cacher
|
||||
* application.net.Ajax.get_URL_cache<br />
|
||||
* application.net.wiki<br />
|
||||
* wiki_API.cache() CeL.wiki.cache()
|
||||
*/
|
||||
|
||||
if (false) {
|
||||
// examples
|
||||
|
||||
CeL.wiki.cache({
|
||||
type : 'page',
|
||||
file_name : 'file_name',
|
||||
list : 'WP:SB',
|
||||
operator : function(data) {
|
||||
console.log(data);
|
||||
}
|
||||
}, function callback(data) {
|
||||
console.log(data);
|
||||
}, {
|
||||
// default options === this
|
||||
// namespace : '0|1',
|
||||
// [KEY_SESSION]
|
||||
// session : wiki,
|
||||
// title_prefix : 'Template:',
|
||||
// cache path prefix
|
||||
prefix : 'base_directory/'
|
||||
});
|
||||
|
||||
CeL.set_debug(6);
|
||||
CeL.wiki.cache({
|
||||
type : 'callback',
|
||||
file_name : 'file_name',
|
||||
list : function(callback) {
|
||||
callback([ 1, 2, 3 ]);
|
||||
},
|
||||
operator : function(data) {
|
||||
console.log(data);
|
||||
}
|
||||
}, function callback(data) {
|
||||
console.log(data);
|
||||
}, {
|
||||
// default options === this
|
||||
// namespace : '0|1',
|
||||
// [KEY_SESSION]
|
||||
// session : wiki,
|
||||
// title_prefix : 'Template:',
|
||||
// cache path prefix
|
||||
prefix : './'
|
||||
});
|
||||
|
||||
CeL.set_debug(6);
|
||||
var wiki = Wiki(true);
|
||||
CeL.wiki.cache({
|
||||
type : 'wdq',
|
||||
file_name : 'countries',
|
||||
list : 'claim[31:6256]',
|
||||
operator : function(list) {
|
||||
// console.log(list);
|
||||
result = list;
|
||||
}
|
||||
}, function callback(list) {
|
||||
// console.log(list);
|
||||
}, {
|
||||
// default options === this
|
||||
// namespace : '0|1',
|
||||
// [KEY_SESSION]
|
||||
session : wiki,
|
||||
// title_prefix : 'Template:',
|
||||
// cache path prefix
|
||||
prefix : './'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* cache 作業操作之輔助套裝函數。
|
||||
*
|
||||
* 注意: only for node.js. 必須自行 include 'application.platform.nodejs'。 <code>
|
||||
CeL.run('application.platform.nodejs');
|
||||
* </code><br />
|
||||
* 注意: 需要自行先創建各 type 之次目錄,如 page, redirects, embeddedin, ...<br />
|
||||
* 注意: 會改變 operation, _this! Warning: will modify operation, _this!
|
||||
*
|
||||
* 連續作業: 依照 _this 設定 {Object}default options,即傳遞於各 operator 間的 ((this))。<br />
|
||||
* 依照 operation 順序個別執行單一項作業。
|
||||
*
|
||||
* 單一項作業流程:<br />
|
||||
* 設定檔名。<br />
|
||||
* 若不存在此檔,則:<br />
|
||||
* >>> 依照 operation.type 與 operation.list 取得資料。<br />
|
||||
* >>> 若 Array.isArray(operation.list) 則處理多項列表作業:<br />
|
||||
* >>>>>> 個別處理單一項作業,每次執行 operation.each() || operation.each_retrieve()。<br />
|
||||
* >>> 執行 data = operation.retrieve(data),以其回傳作為將要 cache 之 data。<br />
|
||||
* >>> 寫入cache。<br />
|
||||
* 執行 operation.operator(data)
|
||||
*
|
||||
* TODO: file_stream<br />
|
||||
* TODO: do not write file
|
||||
*
|
||||
* @param {Object|Array}operation
|
||||
* 作業設定。
|
||||
* @param {Function}[callback]
|
||||
* 所有作業(operation)執行完後之回調函數。 callback(response data)
|
||||
* @param {Object}[_this]
|
||||
* 傳遞於各 operator 間的 ((this))。注意: 會被本函數更動!
|
||||
*/
|
||||
function wiki_API_cache(operation, callback, _this) {
|
||||
if (library_namespace.is_Object(callback) && !_this) {
|
||||
// 未設定/不設定 callback
|
||||
// shift arguments.
|
||||
_this = callback;
|
||||
callback = undefined;
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
/**
|
||||
* 連續作業時,轉到下一作業。
|
||||
*
|
||||
* node.js v0.11.16: In strict mode code, functions can only be declared
|
||||
* at top level or immediately within another function.
|
||||
*/
|
||||
function next_operator(data) {
|
||||
library_namespace.debug('處理連續作業序列,轉到下一作業: ' + (index + 1) + '/'
|
||||
+ operation.length, 2, 'wiki_API_cache.next_operator');
|
||||
// [ {Object}operation, {Object}operation, ... ]
|
||||
// operation = { type:'embeddedin', operator:function(data) }
|
||||
if (index < operation.length) {
|
||||
var this_operation = operation[index++];
|
||||
// console.log(this_operation);
|
||||
if (!this_operation) {
|
||||
// Allow null operation.
|
||||
library_namespace.debug('未設定 operation[' + (index - 1)
|
||||
+ ']。Skip this operation.', 1,
|
||||
'wiki_API_cache.next_operator');
|
||||
next_operator(data);
|
||||
|
||||
} else {
|
||||
if (!('list' in this_operation)) {
|
||||
// use previous data as list.
|
||||
library_namespace.debug(
|
||||
'未特別指定 list,以前一次之回傳 data 作為 list。', 3,
|
||||
'wiki_API_cache.next_operator');
|
||||
library_namespace.debug('前一次之回傳 data: '
|
||||
+ (data && JSON.stringify(data).slice(0, 180))
|
||||
+ '...', 3, 'wiki_API_cache.next_operator');
|
||||
this_operation.list = data;
|
||||
}
|
||||
if (data) {
|
||||
library_namespace.debug('設定 .last_data_got: '
|
||||
+ (data && JSON.stringify(data).slice(0, 180))
|
||||
+ '...', 3, 'wiki_API_cache.next_operator');
|
||||
this_operation.last_data_got = data;
|
||||
}
|
||||
// default options === _this: 傳遞於各 operator 間的 ((this))。
|
||||
wiki_API_cache(this_operation, next_operator, _this);
|
||||
}
|
||||
|
||||
} else if (typeof callback === 'function') {
|
||||
if (false && Array.isArray(data)) {
|
||||
// TODO: adapt to {Object}operation
|
||||
library_namespace.log('wiki_API_cache: Get ' + data.length
|
||||
+ ' page(s).');
|
||||
// 自訂list
|
||||
// data = [ '' ];
|
||||
if (_this.limit >= 0) {
|
||||
// 設定此初始值,可跳過之前已經處理過的。
|
||||
data = data.slice(0 * _this.limit, 1 * _this.limit);
|
||||
}
|
||||
library_namespace.debug(data.slice(0, 8).map(
|
||||
wiki_API.title_of).join('\n')
|
||||
+ '\n...');
|
||||
}
|
||||
|
||||
// last 收尾
|
||||
callback.call(_this, data);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(operation)) {
|
||||
next_operator();
|
||||
return;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
/**
|
||||
* 以下為處理單一次作業。
|
||||
*/
|
||||
library_namespace.debug('處理單一次作業。', 2, 'wiki_API_cache');
|
||||
library_namespace.debug(
|
||||
'using operation: ' + JSON.stringify(operation), 6,
|
||||
'wiki_API_cache');
|
||||
|
||||
if (typeof _this !== 'object') {
|
||||
// _this: 傳遞於各 operator 間的 ((this))。
|
||||
_this = Object.create(null);
|
||||
}
|
||||
|
||||
var file_name = operation.file_name,
|
||||
/** 前一次之回傳 data。每次產出的 data。 */
|
||||
last_data_got = operation.last_data_got;
|
||||
|
||||
if (typeof file_name === 'function') {
|
||||
// @see wiki_API_cache.title_only
|
||||
file_name = file_name.call(_this, last_data_got, operation);
|
||||
}
|
||||
|
||||
var
|
||||
/** {String}method to get data */
|
||||
type = operation.type,
|
||||
/** {Boolean}是否自動嘗試建立目錄。 */
|
||||
try_mkdir = typeof library_namespace.fs_mkdir === 'function'
|
||||
&& operation.mkdir,
|
||||
//
|
||||
operator = typeof operation.operator === 'function'
|
||||
&& operation.operator,
|
||||
//
|
||||
list = operation.list;
|
||||
|
||||
if (!file_name) {
|
||||
// 若自行設定了檔名,則慢點執行 list(),先讀讀 cache。因為 list() 可能會頗耗時間。
|
||||
// 基本上,設定 this.* 應該在 operation.operator() 中,而不是在 operation.list() 中。
|
||||
if (typeof list === 'function') {
|
||||
// TODO: 允許非同步方法。
|
||||
list = list.call(_this, last_data_got, operation);
|
||||
}
|
||||
|
||||
if (!operation.postfix) {
|
||||
if (type === 'file')
|
||||
operation.postfix = '.txt';
|
||||
else if (type === 'URL')
|
||||
operation.postfix = '.htm';
|
||||
}
|
||||
|
||||
// 自行設定之檔名 operation.file_name 優先度較 type/title 高。
|
||||
// 需要自行創建目錄!
|
||||
file_name = _this[type + '_prefix'] || type;
|
||||
file_name = [ file_name
|
||||
// treat file_name as directory
|
||||
? /[\\\/]/.test(file_name) ? file_name : file_name + '/' : '',
|
||||
//
|
||||
wiki_API.is_page_data(list) ? list.title
|
||||
// 若 Array.isArray(list),則 ((file_name = ''))。
|
||||
: typeof list === 'string' && wiki_API.normalize_title(list, true) ];
|
||||
if (file_name[1]) {
|
||||
file_name = file_name[0]
|
||||
// 正規化檔名。
|
||||
+ file_name[1].replace(/\//g, '_');
|
||||
} else {
|
||||
// assert: node_fs.readFile('') 將執行 callback(error)
|
||||
file_name = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (file_name) {
|
||||
if (!('postfix' in operation) && !('postfix' in _this)
|
||||
&& /\.[a-z\d\-]+$/i.test(file_name)) {
|
||||
// 若已設定 filename extension,則不自動添加。
|
||||
operation.postfix = '';
|
||||
}
|
||||
|
||||
file_name = [ 'prefix' in operation ? operation.prefix
|
||||
// _this.prefix: cache path prefix
|
||||
: 'prefix' in _this
|
||||
//
|
||||
? _this.prefix : wiki_API_cache.prefix, file_name,
|
||||
// auto detect filename extension
|
||||
'postfix' in operation ? operation.postfix
|
||||
//
|
||||
: 'postfix' in _this ? _this.postfix : wiki_API_cache.postfix ];
|
||||
library_namespace.debug('Pre-normalized cache file name: ['
|
||||
+ file_name + ']', 5, 'wiki_API_cache');
|
||||
if (false)
|
||||
library_namespace.debug('file name param:'
|
||||
+ [ operation.file_name, _this[type + '_prefix'], type,
|
||||
JSON.stringify(list) ].join(';'), 6,
|
||||
'wiki_API_cache');
|
||||
// 正規化檔名。
|
||||
file_name = file_name.join('').replace(/[:*?<>]/g, '_');
|
||||
}
|
||||
library_namespace.debug('Try to read cache file: [' + file_name + ']',
|
||||
3, 'wiki_API_cache');
|
||||
|
||||
var
|
||||
/**
|
||||
* 採用 JSON<br />
|
||||
* TODO: parse & stringify 機制
|
||||
*
|
||||
* @type {Boolean}
|
||||
*/
|
||||
using_JSON = 'json' in operation ? operation.json : /\.json$/i
|
||||
.test(file_name),
|
||||
/** {String}file encoding for fs of node.js. */
|
||||
encoding = _this.encoding || wiki_API.encoding;
|
||||
// list file path
|
||||
_this.file_name = file_name;
|
||||
|
||||
// console.log('Read file: ' + file_name);
|
||||
node_fs.readFile(file_name, encoding, function(error, data) {
|
||||
/**
|
||||
* 結束作業。
|
||||
*/
|
||||
function finish_work(data) {
|
||||
library_namespace.debug('finish work', 3,
|
||||
'wiki_API_cache.finish_work');
|
||||
last_data_got = data;
|
||||
if (operator)
|
||||
operator.call(_this, data, operation);
|
||||
library_namespace.debug('loading callback', 3,
|
||||
'wiki_API_cache.finish_work');
|
||||
if (typeof callback === 'function')
|
||||
callback.call(_this, data);
|
||||
}
|
||||
|
||||
if (!operation.reget && !error && (data ||
|
||||
// 當資料 Invalid,例如採用 JSON 卻獲得空資料時;則視為 error,不接受此資料。
|
||||
('accept_empty_data' in _this
|
||||
//
|
||||
? _this.accept_empty_data : !using_JSON))) {
|
||||
// gettext_config:{"id":"using-cached-data"}
|
||||
library_namespace.debug('Using cached data.', 3,
|
||||
'wiki_API_cache');
|
||||
library_namespace.debug('Cached data: ['
|
||||
+ (data && data.slice(0, 200)) + ']...', 5,
|
||||
'wiki_API_cache');
|
||||
if (using_JSON && data) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
library_namespace.error(
|
||||
// error. e.g., "undefined"
|
||||
'wiki_API_cache: Cannot parse as JSON: ' + data);
|
||||
// 注意: 若中途 abort,此時可能需要手動刪除大小為 0 的 cache file!
|
||||
data = undefined;
|
||||
}
|
||||
}
|
||||
finish_work(data);
|
||||
return;
|
||||
}
|
||||
|
||||
library_namespace.debug(
|
||||
operation.reget ? 'Dispose cache. Reget again.'
|
||||
// ↑ operation.reget: 放棄 cache,重新取得資料。
|
||||
: 'No valid cached data. Try to get data...', 3,
|
||||
'wiki_API_cache');
|
||||
|
||||
/**
|
||||
* 寫入 cache 至檔案系統。
|
||||
*/
|
||||
function write_cache(data) {
|
||||
if (operation.cache === false) {
|
||||
// 當設定 operation.cache: false 時,不寫入 cache。
|
||||
library_namespace.debug(
|
||||
'設定 operation.cache === false,不寫入 cache。', 3,
|
||||
'wiki_API_cache.write_cache');
|
||||
|
||||
} else if (/[^\\\/]$/.test(file_name)) {
|
||||
library_namespace.info('wiki_API_cache: '
|
||||
+ 'Write cache data to [' + file_name + '].'
|
||||
+ (using_JSON ? ' (using JSON)' : ''));
|
||||
library_namespace.debug('Cache data: '
|
||||
+ (data && JSON.stringify(data).slice(0, 190))
|
||||
+ '...', 3, 'wiki_API_cache.write_cache');
|
||||
var write = function() {
|
||||
// 為了預防需要建立目錄,影響到後面的作業,
|
||||
// 因此採用 fs.writeFileSync() 而非 fs.writeFile()。
|
||||
node_fs.writeFileSync(file_name, using_JSON ? JSON
|
||||
.stringify(data) : data, encoding);
|
||||
};
|
||||
try {
|
||||
write();
|
||||
} catch (error) {
|
||||
// assert: 此 error.code 表示上層目錄不存在。
|
||||
var matched = error.code === 'ENOENT'
|
||||
// 未設定 operation.mkdir 的話,預設會自動嘗試建立目錄。
|
||||
&& try_mkdir !== false
|
||||
//
|
||||
&& file_name.match(/[\\\/][^\\\/]+$/);
|
||||
if (matched) {
|
||||
// 僅測試一次。設定 "已嘗試過" flag。
|
||||
try_mkdir = false;
|
||||
// create parent directory
|
||||
library_namespace.fs_mkdir(file_name.slice(0,
|
||||
matched.index));
|
||||
// re-write file again.
|
||||
try {
|
||||
write();
|
||||
} catch (e) {
|
||||
library_namespace.error(
|
||||
//
|
||||
'wiki_API_cache: Error to write cache data!');
|
||||
library_namespace.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finish_work(data);
|
||||
}
|
||||
|
||||
// node.js v0.11.16: In strict mode code, functions can only be
|
||||
// declared
|
||||
// at top level or immediately within another function.
|
||||
/**
|
||||
* 取得並處理下一項 data。
|
||||
*/
|
||||
function get_next_item(data) {
|
||||
if (index < list.length) {
|
||||
// 利用基本相同的參數以取得 cache。
|
||||
_operation.list = list[index++];
|
||||
var message = '處理多項列表作業: ' + type + ' ' + index + '/'
|
||||
+ list.length;
|
||||
if (list.length > 8) {
|
||||
library_namespace.info('wiki_API_cache.get_next_item: '
|
||||
+ message);
|
||||
} else {
|
||||
library_namespace.debug(message, 1,
|
||||
'wiki_API_cache.get_next_item');
|
||||
}
|
||||
wiki_API_cache(_operation, get_next_item, _this);
|
||||
} else {
|
||||
// last 收尾
|
||||
// All got. retrieve data.
|
||||
if (_operation.data_list)
|
||||
data = _operation.data_list;
|
||||
if (typeof operation.retrieve === 'function')
|
||||
data = operation.retrieve.call(_this, data);
|
||||
write_cache(data);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof list === 'function' && type !== 'callback') {
|
||||
library_namespace.debug('Call .list()', 3, 'wiki_API_cache');
|
||||
list = list.call(_this, last_data_got, operation);
|
||||
// 對於 .list() 為 asynchronous 函數的處理。
|
||||
if (list === wiki_API_cache.abort) {
|
||||
library_namespace.debug('It seems the .list()'
|
||||
+ ' is an asynchronous function.' + ' I will exit'
|
||||
+ ' and wait for the .list() finished.', 3,
|
||||
'wiki_API_cache');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (list === wiki_API_cache.abort) {
|
||||
library_namespace
|
||||
.debug('Abort operation.', 1, 'wiki_API_cache');
|
||||
finish_work();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(list)) {
|
||||
if (!type) {
|
||||
library_namespace.debug('採用 list (length ' + list.length
|
||||
+ ') 作為 data。', 1, 'wiki_API_cache');
|
||||
write_cache(list);
|
||||
return;
|
||||
}
|
||||
if (list.length > 1e6) {
|
||||
library_namespace.warn(
|
||||
//
|
||||
'wiki_API_cache: 警告: list 過長/超過限度 (length ' + list.length
|
||||
+ '),將過於耗時而不實際!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 處理多項列表作業。
|
||||
*/
|
||||
var index = 0, _operation = Object.clone(operation);
|
||||
// 個別頁面不設定 .file_name, .end。
|
||||
delete _operation.end;
|
||||
if (_operation.each_file_name) {
|
||||
_operation.file_name = _operation.each_file_name;
|
||||
delete _operation.each_file_name;
|
||||
} else {
|
||||
delete _operation.file_name;
|
||||
}
|
||||
if (typeof _operation.each === 'function') {
|
||||
// 每一項 list 之項目執行一次 .each()。
|
||||
_operation.operator = _operation.each;
|
||||
delete _operation.each;
|
||||
} else {
|
||||
if (typeof _operation.each_retrieve === 'function')
|
||||
_operation.each_retrieve = _operation.each_retrieve
|
||||
.bind(_this);
|
||||
else
|
||||
delete _operation.each_retrieve;
|
||||
/**
|
||||
* 預設處理列表的函數。
|
||||
*/
|
||||
_operation.operator = function(data) {
|
||||
if ('each_retrieve' in operation)
|
||||
// 資料事後處理程序 (post-processor):
|
||||
// 將以 .each_retrieve() 的回傳作為要處理的資料。
|
||||
data = operation.each_retrieve.call(_this, data);
|
||||
if (_operation.data_list) {
|
||||
if (Array.isArray(data))
|
||||
Array.prototype.push.apply(
|
||||
_operation.data_list, data);
|
||||
else if (data)
|
||||
_operation.data_list.push(data);
|
||||
} else {
|
||||
if (Array.isArray(data))
|
||||
_operation.data_list = data;
|
||||
else if (data)
|
||||
_operation.data_list = [ data ];
|
||||
}
|
||||
};
|
||||
}
|
||||
library_namespace.debug('處理多項列表作業, using operation: '
|
||||
+ JSON.stringify(_operation), 5, 'wiki_API_cache');
|
||||
|
||||
get_next_item();
|
||||
return;
|
||||
}
|
||||
|
||||
// ------------------------------------------------
|
||||
/**
|
||||
* 以下為處理單一項作業。
|
||||
*/
|
||||
|
||||
var to_get_data, list_type;
|
||||
if (// type in get_list.type
|
||||
wiki_API.list.type_list.includes(type)) {
|
||||
list_type = type;
|
||||
type = 'list';
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'callback':
|
||||
if (typeof list !== 'function') {
|
||||
library_namespace
|
||||
.warn('wiki_API_cache: list is not function!');
|
||||
callback.call(_this, last_data_got);
|
||||
break;
|
||||
}
|
||||
// 手動取得資料。使用 list=function(callback){callback(list);}
|
||||
to_get_data = function(list, callback) {
|
||||
library_namespace.log('wiki_API_cache: '
|
||||
+ 'manually get data and then callback(list).');
|
||||
if (typeof list === 'function') {
|
||||
// assert: (typeof list === 'function') 必須自己回 call!
|
||||
list.call(_this, callback, last_data_got, operation);
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case 'file':
|
||||
// 一般不應用到。
|
||||
// get file 內容。
|
||||
to_get_data = function(file_path, callback) {
|
||||
library_namespace.log('wiki_API_cache: Get file ['
|
||||
+ file_path + '].');
|
||||
node_fs.readFile(file_path, operation.encoding, function(
|
||||
error, data) {
|
||||
if (error)
|
||||
library_namespace.error(
|
||||
//
|
||||
'wiki_API_cache: Error get file [' + file_path
|
||||
+ ']: ' + error);
|
||||
callback.call(_this, data);
|
||||
});
|
||||
};
|
||||
break;
|
||||
|
||||
case 'URL':
|
||||
// get URL 頁面內容。
|
||||
to_get_data = function(URL, callback) {
|
||||
library_namespace.log('wiki_API_cache: Get URL of [' + URL
|
||||
+ '].');
|
||||
library_namespace.get_URL(URL, callback);
|
||||
};
|
||||
break;
|
||||
|
||||
case 'wdq':
|
||||
to_get_data = function(query, callback) {
|
||||
if (_this[KEY_SESSION]) {
|
||||
if (!_this[KEY_SESSION].data_session) {
|
||||
_this[KEY_SESSION].set_data();
|
||||
_this[KEY_SESSION].run(function() {
|
||||
// retry again
|
||||
to_get_data(query, callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
operation[KEY_SESSION]
|
||||
//
|
||||
= _this[KEY_SESSION].data_session;
|
||||
}
|
||||
|
||||
library_namespace.log('wiki_API_cache: Wikidata Query ['
|
||||
+ query + '].');
|
||||
// wikidata_query(query, callback, options)
|
||||
wiki_API.wdq(query, callback, operation);
|
||||
};
|
||||
break;
|
||||
|
||||
case 'page':
|
||||
// get page contents 頁面內容。
|
||||
// title=(operation.title_prefix||_this.title_prefix)+operation.list
|
||||
to_get_data = function(title, callback) {
|
||||
library_namespace.log('wiki_API_cache: Get content of '
|
||||
+ wiki_API.title_link_of(title));
|
||||
// 防止汙染。
|
||||
var _options = library_namespace.new_options(_this,
|
||||
operation);
|
||||
// 包含 .list 時,wiki_API.page() 不會自動添加 .prop。
|
||||
delete _options.list;
|
||||
wiki_API.page(title, function(page_data) {
|
||||
callback(page_data);
|
||||
}, _options);
|
||||
};
|
||||
break;
|
||||
|
||||
case 'redirects_here':
|
||||
// 取得所有重定向到(title重定向標的)之頁面列表,(title重定向標的)將會排在[0]。
|
||||
// 注意: 無法避免雙重重定向問題!
|
||||
to_get_data = function(title, callback) {
|
||||
// wiki_API.redirects_here(title, callback, options)
|
||||
wiki_API.redirects_here(title, function(root_page_data,
|
||||
redirect_list) {
|
||||
if (!operation.keep_redirects && redirect_list
|
||||
&& redirect_list[0]) {
|
||||
if (false) {
|
||||
console.assert(redirect_list[0].redirects
|
||||
//
|
||||
.join() === redirect_list.slice(1).join());
|
||||
}
|
||||
// cache 中不需要此累贅之資料。
|
||||
delete redirect_list[0].redirects;
|
||||
delete redirect_list[0].redirect_list;
|
||||
}
|
||||
callback(redirect_list);
|
||||
}, Object.assign({
|
||||
// Making .redirect_list[0] the redirect target.
|
||||
include_root : true
|
||||
}, _this, operation));
|
||||
};
|
||||
break;
|
||||
|
||||
case 'list':
|
||||
to_get_data = function(title, callback) {
|
||||
var options = Object.assign({
|
||||
type : list_type
|
||||
}, _this, operation);
|
||||
wiki_API.list(title, function(pages) {
|
||||
if (!options.for_each_page || options.get_list) {
|
||||
library_namespace.log(list_type
|
||||
// allpages 不具有 title。
|
||||
+ (title ? ' '
|
||||
//
|
||||
+ wiki_API.title_link_of(title) : '') + ': '
|
||||
+ pages.length + ' page(s).');
|
||||
}
|
||||
pages.query_title = title;
|
||||
// page list, title page_data
|
||||
callback(pages);
|
||||
}, options);
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
if (typeof type === 'function')
|
||||
to_get_data = type.bind(Object.assign(Object.create(null),
|
||||
_this, operation));
|
||||
else if (type)
|
||||
throw new Error('wiki_API_cache: Bad type: ' + type);
|
||||
else {
|
||||
library_namespace.debug('直接採用 list 作為 data。', 1,
|
||||
'wiki_API_cache');
|
||||
write_cache(list);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 回復 recover type
|
||||
// if (list_type) type = list_type;
|
||||
|
||||
var title = list;
|
||||
|
||||
if (typeof title === 'string') {
|
||||
// 可以用 operation.title_prefix 覆蓋 _this.title_prefix
|
||||
if ('title_prefix' in operation) {
|
||||
if (operation.title_prefix)
|
||||
title = operation.title_prefix + title;
|
||||
} else if (_this.title_prefix)
|
||||
title = _this.title_prefix + title;
|
||||
}
|
||||
library_namespace.debug('處理單一項作業: ' + wiki_API.title_link_of(title)
|
||||
+ '。', 3, 'wiki_API_cache');
|
||||
to_get_data(title, write_cache);
|
||||
});
|
||||
}
|
||||
|
||||
/** {String}預設 file encoding for fs of node.js。 */
|
||||
wiki_API.encoding = 'utf8';
|
||||
/** {String}檔名預設前綴。 */
|
||||
wiki_API_cache.prefix = '';
|
||||
/** {String}檔名預設後綴。 */
|
||||
wiki_API_cache.postfix = '.json';
|
||||
/**
|
||||
* 若 operation.list() return wiki_API_cache.abort,<br />
|
||||
* 則將直接中斷離開 operation,不執行 callback。<br />
|
||||
* 此時須由 operation.list() 自行處理 callback。
|
||||
*/
|
||||
wiki_API_cache.abort = typeof Symbol === 'function' ? Symbol('ABORT_CACHE')
|
||||
//
|
||||
: {
|
||||
cache : 'abort'
|
||||
};
|
||||
/**
|
||||
* 只取檔名,僅用在 operation.each_file_name。<br />
|
||||
* <code>{
|
||||
* each_file_name : CeL.wiki.cache.title_only,
|
||||
* }</code>
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
wiki_API_cache.title_only = function(last_data_got, operation) {
|
||||
var list = operation.list;
|
||||
if (typeof list === 'function') {
|
||||
operation.list = list = list.call(this, last_data_got, operation);
|
||||
}
|
||||
return operation.type + '/' + remove_page_title_namespace(list);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
// wiki_API.cache = wiki_API_cache;
|
||||
return wiki_API_cache;
|
||||
}
|
6212
app/node_modules/cejs/application/net/wiki/data.js
generated
vendored
Normal file
6212
app/node_modules/cejs/application/net/wiki/data.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1646
app/node_modules/cejs/application/net/wiki/edit.js
generated
vendored
Normal file
1646
app/node_modules/cejs/application/net/wiki/edit.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
483
app/node_modules/cejs/application/net/wiki/featured_content.js
generated
vendored
Normal file
483
app/node_modules/cejs/application/net/wiki/featured_content.js
generated
vendored
Normal file
@@ -0,0 +1,483 @@
|
||||
/**
|
||||
* @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 strict';
|
||||
// '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#<23>]+)(?:\|([^\[\]\|<7C>]*))?\]\]'''|\n==([^=].*?)==\n/g)
|
||||
// 特色列表: [[:title]]
|
||||
|| test_pattern(/\[\[:([^{}\[\]\|<>\t\n#<23>]+)(?:\|([^\[\]\|<7C>]*))?\]\]|\n==([^=].*?)==\n/g)
|
||||
// 優良條目轉換到子頁面模式: 警告:本頁中的所有嵌入頁面都會被機器人當作優良條目的分類列表。請勿嵌入非優良條目的分類列表。
|
||||
|| test_pattern(/{{(Wikipedia:[^{}\|]+)}}/g, 10)
|
||||
// 優良條目子分類列表, 已撤消的優良條目: all links NOT starting with ':'
|
||||
|| /\[\[([^{}\[\]\|<>\t\n#<23>:][^{}\[\]\|<>\t\n#<23>]*)(?:\|([^\[\]\|<7C>]*))?\]\]|\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) {
|
||||
throw new Error('Unknown type: ' + options.type);
|
||||
}
|
||||
|
||||
// ----------------------------
|
||||
|
||||
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;
|
||||
}
|
2859
app/node_modules/cejs/application/net/wiki/list.js
generated
vendored
Normal file
2859
app/node_modules/cejs/application/net/wiki/list.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4003
app/node_modules/cejs/application/net/wiki/namespace.js
generated
vendored
Normal file
4003
app/node_modules/cejs/application/net/wiki/namespace.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4723
app/node_modules/cejs/application/net/wiki/page.js
generated
vendored
Normal file
4723
app/node_modules/cejs/application/net/wiki/page.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
320
app/node_modules/cejs/application/net/wiki/page/Page.js
generated
vendored
Normal file
320
app/node_modules/cejs/application/net/wiki/page/Page.js
generated
vendored
Normal file
@@ -0,0 +1,320 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科): Page
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* TODO:<code>
|
||||
|
||||
</code>
|
||||
*
|
||||
* @since
|
||||
*
|
||||
* @see https://mwn.toolforge.org/docs/interfaces/_page_.mwnpage.html
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.page.Page',
|
||||
|
||||
require : 'data.code.compatibility.'
|
||||
//
|
||||
+ '|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 get_namespace = wiki_API.namespace;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
if (false) {
|
||||
// call new_Page()
|
||||
page = wiki_session.Page(page_title);
|
||||
// {Number}p.ns
|
||||
// {String}p.title
|
||||
|
||||
// await page.backlinks({get_list:true}) will get {Array}list.
|
||||
// page.backlinks() is asyncIterator
|
||||
//
|
||||
// https://www.codementor.io/@tiagolopesferreira/asynchronous-iterators-in-javascript-jl1yg8la1
|
||||
// https://stackoverflow.com/questions/55531247/using-javascripts-symbol-asynciterator-with-for-await-of-loop
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
|
||||
// for await (const page_data of page.backlinks()) {
|
||||
// console.log(page_data); }
|
||||
|
||||
// TODO:
|
||||
|
||||
// typeof await(page.content() || page.read()) === 'string';
|
||||
|
||||
typeof page.wikitext === 'string';
|
||||
|
||||
// page.fetch() is asyncIterator 泛用方法 相當於 wiki.query()
|
||||
|
||||
// page.revisions() is asyncIterator
|
||||
|
||||
// typeof await(page.is_biography()) === 'boolean';
|
||||
}
|
||||
|
||||
function Page(page_title, options, session) {
|
||||
this[KEY_SESSION] = session;
|
||||
|
||||
if (wiki_API.is_page_data(page_title)) {
|
||||
Object.assign(this, page_title);
|
||||
return;
|
||||
}
|
||||
|
||||
// page_data 之 structure 按照 wiki API 本身之 return
|
||||
// page_data = {pageid,ns,title,revisions:[{revid,timestamp,'*'}]}
|
||||
Object.assign(this, {
|
||||
// pageid : 0,
|
||||
ns : session.namespace(page_title) || 0,
|
||||
title : session.normalize_title(page_title)
|
||||
});
|
||||
}
|
||||
|
||||
function set_options_session(options) {
|
||||
var session = this[KEY_SESSION];
|
||||
options = wiki_API.add_session_to_options(session, options);
|
||||
return options;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
function Page__content(options) {
|
||||
options = set_options_session.call(this, options);
|
||||
if (this.revisions) {
|
||||
return wiki_API.content_of(this, options);
|
||||
}
|
||||
|
||||
var promise = new Promise(function executor(resolve, reject) {
|
||||
wiki_API.page(wiki_API.is_page_data(this) ? this : this.title,
|
||||
//
|
||||
function(page_data, error) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
if (!page_data.revisions) {
|
||||
reject(new Error('No .revisions get!'));
|
||||
return;
|
||||
}
|
||||
Object.assign(this, page_data);
|
||||
// console.trace(this);
|
||||
return resolve(wiki_API.content_of(this, options));
|
||||
}.bind(this), options);
|
||||
}.bind(this));
|
||||
return promise;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
function Page__check_stop(options) {
|
||||
// Copy from wiki_API.prototype.next.edit
|
||||
var promise = new Promise(function executor(resolve, reject) {
|
||||
var session = this[KEY_SESSION];
|
||||
options.token = session.token;
|
||||
wiki_API.check_stop(function(stopped) {
|
||||
session.stopped = stopped;
|
||||
resolve(stopped);
|
||||
}, options);
|
||||
}.bind(this));
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function Page__edit(content, options) {
|
||||
options = set_options_session.call(this, options);
|
||||
var session = this[KEY_SESSION];
|
||||
|
||||
// Copy from wiki_API.prototype.next.edit
|
||||
var promise = new Promise(function executor(resolve, reject) {
|
||||
if (session.stopped && !options.skip_stopped) {
|
||||
library_namespace.warn('Page__edit: 已停止作業,放棄編輯'
|
||||
+ wiki_API.title_link_of(this) + '!');
|
||||
reject(new Error('放棄編輯'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.is_Flow) {
|
||||
reject(new Error(new Error('NYI: flow page')));
|
||||
}
|
||||
|
||||
if (options.skip_nochange
|
||||
// 採用 skip_nochange 可以跳過實際 edit 的動作。
|
||||
&& content === wiki_API.content_of(this)) {
|
||||
library_namespace.debug('Skip '
|
||||
//
|
||||
+ wiki_API.title_link_of(this)
|
||||
// 'nochange', no change
|
||||
+ ': The same content.', 1, 'Page__edit');
|
||||
resolve('The same content');
|
||||
return;
|
||||
}
|
||||
|
||||
// console.trace([ this, wiki_API.is_page_data(this), session.token
|
||||
// ]);
|
||||
wiki_API.edit([ session.API_URL,
|
||||
//
|
||||
wiki_API.is_page_data(this) ? this : this.title ], content,
|
||||
//
|
||||
session.token, options, function wiki_API_Page_edit_callback(title,
|
||||
error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
}.bind(this));
|
||||
|
||||
if (!('stopped' in session)) {
|
||||
promise = Page__check_stop.call(this, options).then(promise);
|
||||
}
|
||||
|
||||
if (typeof content === 'function')
|
||||
return this.content().then(promise);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
function Page__list(options) {
|
||||
options = set_options_session.call(this, options);
|
||||
// options.type, options[KEY_SESSION] are setted in Page__list_async()
|
||||
var promise = new Promise(function executor(resolve, reject) {
|
||||
wiki_API.list(wiki_API.is_page_data(this) ? this : this.title,
|
||||
//
|
||||
function(pages, target, options) {
|
||||
if (pages.error)
|
||||
reject(pages.error);
|
||||
else
|
||||
resolve(pages);
|
||||
}, options);
|
||||
}.bind(this));
|
||||
return promise;
|
||||
}
|
||||
|
||||
var Symbol_asyncIterator = typeof Symbol === 'function'
|
||||
&& Symbol.asyncIterator;
|
||||
|
||||
var done_object = {
|
||||
// value : generator.page_count,
|
||||
done : true
|
||||
};
|
||||
|
||||
function Page__list_async(method, options) {
|
||||
options = set_options_session.call(this, options);
|
||||
options.type = method;
|
||||
if (!Symbol_asyncIterator || options && options.get_list) {
|
||||
return Page__list.call(this, options);
|
||||
}
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
var list_generator = Object.create(null);
|
||||
list_generator[Symbol_asyncIterator] = (function() {
|
||||
function get_next_object() {
|
||||
return {
|
||||
value : generator.queue.shift(),
|
||||
done : false
|
||||
};
|
||||
}
|
||||
|
||||
var generator = {
|
||||
queue : [],
|
||||
next : function() {
|
||||
if (generator.resolve) {
|
||||
throw new Error(
|
||||
'Call resolve() before latest promise resolved');
|
||||
}
|
||||
|
||||
if (generator.queue.length > 0) {
|
||||
// 執行順序3: 中間最多的是這個程序一直反覆 loop
|
||||
return Promise.resolve(get_next_object());
|
||||
}
|
||||
|
||||
// assert: generator.queue.length === 0
|
||||
if (generator.done) {
|
||||
// 執行順序4: 最後一次 iterate
|
||||
return Promise.resolve(done_object);
|
||||
}
|
||||
|
||||
// 執行順序1
|
||||
return new Promise(function(resolve, reject) {
|
||||
generator.resolve = resolve;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
options.for_each_page = function(item) {
|
||||
generator.queue.push(item);
|
||||
var resolve = generator.resolve;
|
||||
if (resolve) {
|
||||
delete generator.resolve;
|
||||
// 執行順序2
|
||||
resolve(get_next_object());
|
||||
}
|
||||
};
|
||||
wiki_API.list(wiki_API.is_page_data(this) ? this : this.title,
|
||||
//
|
||||
function(list) {
|
||||
// generator.page_count = list.length;
|
||||
generator.done = true;
|
||||
var resolve = generator.resolve;
|
||||
if (resolve) {
|
||||
// 基本上不會執行到這邊 @ node.js
|
||||
delete generator.resolve;
|
||||
resolve(done_object);
|
||||
}
|
||||
}, options);
|
||||
|
||||
return generator;
|
||||
}).bind(this);
|
||||
return list_generator;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
Object.assign(wiki_API.prototype, {
|
||||
new_Page : function new_Page(page_title, options) {
|
||||
return new Page(page_title, options,/* session */this);
|
||||
},
|
||||
Page : Page
|
||||
});
|
||||
|
||||
wiki_API.list.type_list.forEach(function(method) {
|
||||
// if (!method.includes('all'))
|
||||
Page.prototype[method] = function Page__list_frontend(options) {
|
||||
return Page__list_async.call(this, method, options);
|
||||
};
|
||||
});
|
||||
|
||||
Object.assign(Page.prototype, {
|
||||
content : Page__content,
|
||||
edit : Page__edit
|
||||
});
|
||||
|
||||
return Page;
|
||||
}
|
2078
app/node_modules/cejs/application/net/wiki/parser.js
generated
vendored
Normal file
2078
app/node_modules/cejs/application/net/wiki/parser.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1567
app/node_modules/cejs/application/net/wiki/parser/evaluate.js
generated
vendored
Normal file
1567
app/node_modules/cejs/application/net/wiki/parser/evaluate.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2145
app/node_modules/cejs/application/net/wiki/parser/misc.js
generated
vendored
Normal file
2145
app/node_modules/cejs/application/net/wiki/parser/misc.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2066
app/node_modules/cejs/application/net/wiki/parser/section.js
generated
vendored
Normal file
2066
app/node_modules/cejs/application/net/wiki/parser/section.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3971
app/node_modules/cejs/application/net/wiki/parser/wikitext.js
generated
vendored
Normal file
3971
app/node_modules/cejs/application/net/wiki/parser/wikitext.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1109
app/node_modules/cejs/application/net/wiki/query.js
generated
vendored
Normal file
1109
app/node_modules/cejs/application/net/wiki/query.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4305
app/node_modules/cejs/application/net/wiki/task.js
generated
vendored
Normal file
4305
app/node_modules/cejs/application/net/wiki/task.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1534
app/node_modules/cejs/application/net/wiki/template_functions.js
generated
vendored
Normal file
1534
app/node_modules/cejs/application/net/wiki/template_functions.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
90
app/node_modules/cejs/application/net/wiki/template_functions/enwiki.js
generated
vendored
Normal file
90
app/node_modules/cejs/application/net/wiki/template_functions/enwiki.js
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科): 常用模板特設功能。本工具檔放置的是指定 wiki
|
||||
* 計畫特有的模板。
|
||||
*
|
||||
* 注意: 本程式庫必須應各 wiki project 模板內容改動而改寫。
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* TODO:<code>
|
||||
|
||||
</code>
|
||||
*
|
||||
* @since 2021/1/24 16:6:50
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.template_functions.enwiki',
|
||||
|
||||
require : 'data.native.'
|
||||
// Should also load essential MediaWiki modules
|
||||
+ '|application.net.wiki.',
|
||||
|
||||
// 設定不匯出的子函式。
|
||||
no_extend : 'this,*',
|
||||
|
||||
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
|
||||
code : module_code
|
||||
});
|
||||
|
||||
function module_code(library_namespace) {
|
||||
|
||||
// requiring
|
||||
var wiki_API = library_namespace.application.net.wiki;
|
||||
// @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;
|
||||
|
||||
// e.g., 'zhwiki'
|
||||
var module_site_name = this.id.match(/[^.]+$/)[0];
|
||||
|
||||
function empty_string(/* options */) {
|
||||
// var template_token = this;
|
||||
return '';
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// template_token.expand() 可將模板轉換成一般 wiki 語法。
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=expandtemplates
|
||||
// 用於 function preprocess_section_link_token()。
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// Not completed! Only for get_all_anchors().
|
||||
function expand_template_Football_box(options) {
|
||||
var parameters = this.parameters;
|
||||
// [[Module:Football box]]
|
||||
return '<div id="' + (parameters.id || '') + '">'
|
||||
// TODO: The content is skipped.
|
||||
+ '</div>';
|
||||
}
|
||||
|
||||
function parse_template_Football_box(template_token, index, parent, options) {
|
||||
template_token.expand = expand_template_Football_box;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
wiki_API.template_functions.functions_of_site[module_site_name] = {
|
||||
// 一些會添加 anchors 的特殊模板。
|
||||
'Football box' : parse_template_Football_box
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不設定(hook)本 module 之 namespace,僅執行 module code。
|
||||
return library_namespace.env.not_to_extend_keyword;
|
||||
}
|
595
app/node_modules/cejs/application/net/wiki/template_functions/general_functions.js
generated
vendored
Normal file
595
app/node_modules/cejs/application/net/wiki/template_functions/general_functions.js
generated
vendored
Normal file
@@ -0,0 +1,595 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科):
|
||||
* 常用模板特設功能。本工具檔放置的是幾乎所有wiki計畫通用的模板,或者少數wiki計畫特有、且大量使用的著名模板。
|
||||
*
|
||||
* 注意: 本程式庫必須應各 wiki project 模板內容改動而改寫。
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* <code>
|
||||
|
||||
TODO:
|
||||
[[w:en:Wikipedia:Database reports/Templates transcluded on the most pages]]
|
||||
[[w:en:Wikipedia:High-risk templates]]
|
||||
|
||||
</code>
|
||||
*
|
||||
* @see [[Special:MostTranscludedPages]], [[Template:High-use]]
|
||||
*
|
||||
* @since 2021/1/24 16:6:50
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.template_functions.general_functions',
|
||||
|
||||
require : 'data.native.'
|
||||
// Should also load essential MediaWiki modules
|
||||
+ '|application.net.wiki.template_functions.',
|
||||
|
||||
// 設定不匯出的子函式。
|
||||
no_extend : 'this,*',
|
||||
|
||||
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
|
||||
code : module_code
|
||||
});
|
||||
|
||||
function module_code(library_namespace) {
|
||||
|
||||
// requiring
|
||||
var wiki_API = library_namespace.application.net.wiki;
|
||||
// @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 empty_string(/* options */) {
|
||||
// var template_token = this;
|
||||
return '';
|
||||
}
|
||||
|
||||
function expand_template_get_parameter_1(options) {
|
||||
var parameters = this.parameters;
|
||||
return parameters[1] ? parameters[1].toString() : '';
|
||||
}
|
||||
|
||||
function trim_param(param) {
|
||||
return param && param.toString().trim();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// template_token.expand() 可將模板轉換成一般 wiki 語法。
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=expandtemplates
|
||||
// 用於 function preprocess_section_link_token()。
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_Void(options) {
|
||||
return '';
|
||||
}
|
||||
|
||||
function parse_template_Void(template_token, index, parent, options) {
|
||||
template_token.expand = expand_template_Void;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// Not completed! Only for get_all_anchors()
|
||||
// @ zh.moegirl [[FLOWERS(Innocent Grey)]]
|
||||
function parse_template_Center(template_token) {
|
||||
template_token.expand = expand_template_get_parameter_1;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// Not completed! Only for get_all_anchors()
|
||||
// @ zh.moegirl [[ARGONAVIS from BanG Dream! 翻唱曲列表]]
|
||||
function parse_template_Font(template_token) {
|
||||
template_token.expand = expand_template_get_parameter_1;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// {{color|英文顏色名稱或是RGB 16進制編碼|文字}}
|
||||
function expand_template_Color(options) {
|
||||
var parameters = this.parameters;
|
||||
return '<span style="color:' + (parameters[1] || '') + '">'
|
||||
+ (parameters[2] || parameters[1] || '') + '</span>';
|
||||
}
|
||||
|
||||
function parse_template_Color(template_token) {
|
||||
template_token.expand = expand_template_Color;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_At(options) {
|
||||
var parameters = this.parameters;
|
||||
return '[[File:At_sign.svg|' + (parameters[1] || 15) + 'px|link=]]';
|
||||
}
|
||||
|
||||
function parse_template_At(template_token) {
|
||||
template_token.expand = expand_template_At;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_User_link(options) {
|
||||
var parameters = this.parameters;
|
||||
return '[[User:' + (parameters[1]) + '|'
|
||||
+ (parameters[2] || parameters[1]) + ']]';
|
||||
}
|
||||
|
||||
function parse_template_User_link(template_token) {
|
||||
template_token.expand = expand_template_User_link;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_module_If_empty(options) {
|
||||
/* const */var token = options && options.template_token_called
|
||||
|| this;
|
||||
/* const */var parameters = token.parameters;
|
||||
// Error.stackTraceLimit = Infinity;
|
||||
// console.trace([ this, parameters, options ]);
|
||||
// console.trace(options && options.template_token_called);
|
||||
for (/* let */var index = 0; index < token.length; index++) {
|
||||
var value = parameters[index];
|
||||
if (value)
|
||||
return value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function parse_module_If_empty(token) {
|
||||
token.expand = expand_module_If_empty;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// Not completed! Only for get_all_anchors()
|
||||
// @ zh.moegirl [[ARGONAVIS from BanG Dream! 翻唱曲列表]]
|
||||
function expand_template_Colored_link(options) {
|
||||
var parameters = this.parameters;
|
||||
// {{Colored link|顏色|頁面名稱鏈接|顯示名稱}}]
|
||||
return '[[:' + parameters[2] + '|<span style="color:' + parameters[1]
|
||||
+ '">' + (parameters[3] || parameters[2]) + '</span>]]';
|
||||
}
|
||||
|
||||
function parse_template_Colored_link(template_token) {
|
||||
template_token.expand = expand_template_Colored_link;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// 一些會添加 anchors 的特殊模板。
|
||||
|
||||
// {{Anchor|anchor|別名1|別名2}}
|
||||
function expand_template_Anchor(options) {
|
||||
var parameters = this.parameters;
|
||||
var wikitext = [];
|
||||
for (/* let */var index = 1; index < this.length; index++) {
|
||||
var anchor = parameters[index];
|
||||
if (!anchor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof anchor !== 'string') {
|
||||
// e.g., `{{Anchor|{{u|Emojibot}}}}` @ zhwiki
|
||||
|
||||
library_namespace.warn('expand_template_Anchor: 特殊 anchor: #'
|
||||
+ anchor);
|
||||
// console.trace(anchor);
|
||||
|
||||
// old jawiki {{Anchor}}
|
||||
// e.g., [[終着駅シリーズ]]: {{Anchor|[[牛尾正直]]}}
|
||||
// {{Anchor|A[[B|C]]}} → "AC"
|
||||
anchor = wiki_API.wikitext_to_plain_text(anchor.toString());
|
||||
}
|
||||
|
||||
// 多空格、斷行會被轉成單一 " "。
|
||||
anchor = anchor.replace(/[\s\n]{2,}/g, ' ');
|
||||
|
||||
// class="anchor"
|
||||
wikitext.push('<span id="' + anchor + '"></span>');
|
||||
}
|
||||
return wikitext.join('');
|
||||
}
|
||||
|
||||
function parse_template_Anchor(template_token, index, parent, options) {
|
||||
template_token.expand = expand_template_Anchor;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_Visible_anchor(options) {
|
||||
var parameters = this.parameters;
|
||||
// {{Visible anchor}}(別名{{Vanc}})は似たテンプレートですが、1個目のリンク先が表示されます。
|
||||
return expand_template_Anchor.call(this, options)
|
||||
// + '<span class="vanchor-text">'
|
||||
+ (parameters.text || parameters[1] || '')
|
||||
// + '</span>'
|
||||
;
|
||||
}
|
||||
|
||||
function parse_template_Visible_anchor(template_token, index, parent,
|
||||
options) {
|
||||
template_token.expand = expand_template_Visible_anchor;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_Term(options) {
|
||||
var parameters = this.parameters;
|
||||
var wikitext = '<dt id="'
|
||||
+ wiki_API.wikitext_to_plain_text(
|
||||
//
|
||||
parameters.id || parameters.term || parameters[1] || '')
|
||||
.replace(/"/g, '').toLowerCase()
|
||||
+ '">'
|
||||
+ (parameters.content || parameters[2] || parameters.term
|
||||
|| parameters[1] || '') + '</dt>';
|
||||
// console.log(wikitext);
|
||||
return wikitext;
|
||||
}
|
||||
|
||||
function parse_template_Term(template_token, index, parent, options) {
|
||||
template_token.expand = expand_template_Term;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_Wikicite(options) {
|
||||
var parameters = this.parameters;
|
||||
// class="citation wikicite"
|
||||
var wikitext = '<cite id='
|
||||
+ (parameters.ref || parameters.id
|
||||
&& ('"Reference-' + parameters.id + '"')) + '>'
|
||||
+ parameters.reference + '</cite>';
|
||||
// console.log(wikitext);
|
||||
return wikitext;
|
||||
}
|
||||
|
||||
function parse_template_Wikicite(template_token, index, parent, options) {
|
||||
template_token.expand = expand_template_Wikicite;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_SfnRef(options) {
|
||||
var parameters = this.parameters;
|
||||
var anchor = 'CITEREF';
|
||||
for (var index = 1; index <= 5 && parameters[index]; index++) {
|
||||
anchor += trim_param(parameters[index]);
|
||||
}
|
||||
// TODO: test year
|
||||
|
||||
// anchor = anchor.replace(/\s+/g, ' ');
|
||||
|
||||
return anchor;
|
||||
}
|
||||
|
||||
function parse_template_SfnRef(template_token, index, parent, options) {
|
||||
template_token.expand = expand_template_SfnRef;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// @see local function sfn (frame) @
|
||||
// https://en.wikipedia.org/wiki/Module:Footnotes
|
||||
function expand_template_Sfn(options) {
|
||||
var parameters = this.parameters;
|
||||
var anchor = 'cite_ref-FOOTNOTE';
|
||||
for (var index = 1; index <= 5 && parameters[index]; index++) {
|
||||
anchor += trim_param(parameters[index]);
|
||||
}
|
||||
|
||||
if (parameters.p)
|
||||
anchor += trim_param(parameters.p);
|
||||
if (parameters.pp)
|
||||
anchor += trim_param(parameters.pp);
|
||||
if (parameters.loc)
|
||||
anchor += trim_param(parameters.loc);
|
||||
|
||||
anchor = anchor.replace(/\s+/g, ' ');
|
||||
|
||||
var wikitext = [];
|
||||
var reference_index = 1;
|
||||
var pointer_index = 0;
|
||||
// TODO: 這個數值必須按照 reference_index 遞增。
|
||||
var ref_anchor = anchor + '-' + reference_index;
|
||||
wikitext.push('<ref name="' + ref_anchor + '">'
|
||||
// TODO: + content
|
||||
+ '</ref>',
|
||||
|
||||
//
|
||||
'<a id="' + anchor + '_' + reference_index + '-'
|
||||
// TODO: 這個數值必須按照 pointer_index 遞增。
|
||||
+ pointer_index + '" href="#' + ref_anchor + '">'
|
||||
// TODO: 這個數值必須按照 reference_index 遞增。
|
||||
+ '[' + reference_index + ']' + '</a>');
|
||||
|
||||
return wikitext.join('');
|
||||
}
|
||||
|
||||
function parse_template_Sfn(template_token, index, parent, options) {
|
||||
template_token.expand = expand_template_Sfn;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// @see createEpisodeNumberCellSecondary() @ [[Module:Episode list]]
|
||||
var EpisodeNumbers = [ 'EpisodeNumber', 'EpisodeNumber2', 'EpisodeNumber3' ];
|
||||
|
||||
function expand_template_Episode_list(options) {
|
||||
// console.trace(this);
|
||||
var parameters = this.parameters;
|
||||
var anchor_prefix = this.anchor_prefix || '';
|
||||
var wikitext = [];
|
||||
for (var index = 0; index < EpisodeNumbers.length; index++) {
|
||||
var anchor = trim_param(parameters[EpisodeNumbers[index]]);
|
||||
// console.trace([ EpisodeNumbers[index], anchor ]);
|
||||
// @see getEpisodeText() @ [[Module:Episode list]]
|
||||
var matched = anchor && anchor.match(/^\w+/);
|
||||
if (matched) {
|
||||
anchor = matched[0];
|
||||
// 極度簡化版。
|
||||
wikitext.push('<th id="' + anchor_prefix + 'ep' + anchor
|
||||
+ '"></th>');
|
||||
}
|
||||
}
|
||||
|
||||
// @see createProductionCodeCell() @ [[Module:Episode list]]
|
||||
var anchor = trim_param(parameters.ProdCode);
|
||||
if (anchor) {
|
||||
wikitext.push('<td id="' + 'pc' + wiki_API.plain_text(anchor)
|
||||
+ '"></td>');
|
||||
}
|
||||
|
||||
// console.trace(wikitext);
|
||||
return wikitext.join('');
|
||||
}
|
||||
|
||||
function parse_template_Episode_list(template_token, index, parent, options) {
|
||||
template_token.expand = expand_template_Episode_list;
|
||||
}
|
||||
|
||||
function expand_template_Episode_table(options) {
|
||||
}
|
||||
|
||||
function parse_template_Episode_table(template_token, index, parent,
|
||||
options) {
|
||||
// template_token.expand = expand_template_Episode_table;
|
||||
|
||||
var parameters = template_token.parameters;
|
||||
var episodes = parameters.episodes;
|
||||
var anchor_prefix = trim_param(parameters.anchor);
|
||||
// console.trace(anchor_prefix);
|
||||
if (anchor_prefix && episodes) {
|
||||
var session = wiki_API.session_of_options(options) || wiki_API;
|
||||
wiki_API.parser.parser_prototype.each.call(episodes,
|
||||
//
|
||||
'transclusion', function(template_token) {
|
||||
if (session.is_template(template_token, [ 'Episode list',
|
||||
'Episode list/sublist' ])) {
|
||||
template_token.anchor_prefix = anchor_prefix;
|
||||
}
|
||||
}, options);
|
||||
}
|
||||
}
|
||||
|
||||
function expand_template_Episode_table__part(options) {
|
||||
var parameters = this.parameters;
|
||||
// console.trace(parameters);
|
||||
|
||||
// [[Module:Episode table]]
|
||||
var id = trim_param(parameters.id);
|
||||
|
||||
if (!id) {
|
||||
// partTypes
|
||||
[ 'Act', 'Chapter', 'Part', 'Volume', 'Week' ].forEach(function(
|
||||
prefix) {
|
||||
var value = parameters[prefix.toLowerCase()];
|
||||
if (value)
|
||||
id = prefix + ' ' + value;
|
||||
});
|
||||
|
||||
if (parameters.subtitle) {
|
||||
id = (id ? id + ': ' : '') + parameters.subtitle;
|
||||
}
|
||||
// console.trace(id);
|
||||
}
|
||||
|
||||
if (id) {
|
||||
return '<td id="' + wiki_API.plain_text(id) + '"></td>';
|
||||
}
|
||||
}
|
||||
|
||||
function parse_template_Episode_table__part(template_token, index, parent,
|
||||
options) {
|
||||
template_token.expand = expand_template_Episode_table__part;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function parse_template_Pin_message(template_token, index, parent, options) {
|
||||
var parameters = template_token.parameters, message_expire_date;
|
||||
if (parameters[1]) {
|
||||
options = library_namespace.new_options(options);
|
||||
options.get_timevalue = true;
|
||||
message_expire_date = wiki_API.parse.date(parameters[1], {
|
||||
get_timevalue : true,
|
||||
});
|
||||
}
|
||||
// console.trace([ message_expire_date, parameters ]);
|
||||
template_token.message_expire_date = message_expire_date || Infinity;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 有缺陷的簡易型 Lua patterns to JavaScript RegExp
|
||||
function Lua_pattern_to_RegExp_pattern(pattern) {
|
||||
return String(pattern).replace(/%l/g, 'a-z').replace(/%u/g, 'A-Z')
|
||||
// e.g., %d, %s, %S, %w, %W
|
||||
.replace(/%/g, '\\');
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Module:Check_for_unknown_parameters
|
||||
function check_template_for_unknown_parameters(template_token, options) {
|
||||
var valid_parameters = this.valid_parameters, valid_RegExp_parameters = this.valid_RegExp_parameters;
|
||||
var invalid_parameters = Object.keys(template_token.parameters)
|
||||
//
|
||||
.filter(function() {
|
||||
if (valid_parameters.has(parameter))
|
||||
return;
|
||||
return !valid_RegExp_parameters.some(function(pattern) {
|
||||
return pattern.test(parameter);
|
||||
});
|
||||
}, this);
|
||||
|
||||
if (invalid_parameters.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var return_value = {
|
||||
invalid_parameters : invalid_parameters
|
||||
};
|
||||
var unknown_text = this.parameters.unknown || 'Found _VALUE_, ';
|
||||
var preview_text = this.parameters.preview;
|
||||
unknown_text = invalid_parameters.map(function(parameter) {
|
||||
return unknown_text.replace(/_VALUE_/g, parameter);
|
||||
}).join('').replace(/[,\s]+$/, '');
|
||||
if (preview_text) {
|
||||
preview_text = invalid_parameters.map(function(parameter) {
|
||||
return preview_text.replace(/_VALUE_/g, parameter);
|
||||
}).join('').replace(/[,\s]+$/, '');
|
||||
}
|
||||
return {
|
||||
invalid_parameters : invalid_parameters,
|
||||
preview_text : preview_text || unknown_text,
|
||||
unknown_text : unknown_text
|
||||
};
|
||||
}
|
||||
|
||||
function parse_module_Check_for_unknown_parameters(token, index, parent,
|
||||
options) {
|
||||
var parameters = token.parameters, valid_parameters = token.valid_parameters = new Set, valid_RegExp_parameters = token.valid_RegExp_parameters = [];
|
||||
for (var index = 1; index < token.length; index++) {
|
||||
var value = parameters[index];
|
||||
if (value)
|
||||
valid_parameters.add(String(value));
|
||||
if (value = parameters['regexp' + index]) {
|
||||
try {
|
||||
value = new RegExp('^'
|
||||
+ Lua_pattern_to_RegExp_pattern(value) + '$');
|
||||
valid_RegExp_parameters.push(value);
|
||||
} catch (e) {
|
||||
library_namespace.error([
|
||||
//
|
||||
'parse_module_Check_for_unknown_parameters: ', {
|
||||
T : [
|
||||
// gettext_config:{"id":"cannot-convert-lua-pattern-to-regexp-pattern-$1"}
|
||||
'Cannot convert Lua pattern to RegExp pattern: %1',
|
||||
//
|
||||
value ]
|
||||
} ]);
|
||||
}
|
||||
}
|
||||
}
|
||||
token.check_template = check_template_for_unknown_parameters
|
||||
.bind(token);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_module_IPAddress(options) {
|
||||
// console.trace(this);
|
||||
var parameters = this.parameters;
|
||||
// console.trace(parameters);
|
||||
if (this.function_name === 'isIp') {
|
||||
// [ , 'IPAddress', 'isIp', '...' ]
|
||||
var is_IP = library_namespace.is_IP(parameters[1]);
|
||||
return is_IP ? String(is_IP) : '';
|
||||
}
|
||||
// TODO:
|
||||
}
|
||||
|
||||
function parse_module_IPAddress(token, index, parent, options) {
|
||||
token.expand = expand_module_IPAddress;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_Template_link(options) {
|
||||
var parameters = this.parameters;
|
||||
return '{{[[Template:' + parameters[1] + '|' + parameters[1]
|
||||
+ ']]}}';
|
||||
}
|
||||
|
||||
function parse_template_Template_link(token, index, parent, options) {
|
||||
token.expand = expand_template_Template_link;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
// general_functions 必須在個別 wiki profiles 之前載入。
|
||||
// 如 CeL.application.net.wiki.template_functions.jawiki 依賴於
|
||||
// general_functions!
|
||||
wiki_API.template_functions.functions_of_all_sites = {
|
||||
Void : parse_template_Void,
|
||||
Center : parse_template_Center,
|
||||
|
||||
// 一些會用於章節標題的特殊模板。 for preprocess_section_link_token()
|
||||
Font : parse_template_Font,
|
||||
Color : parse_template_Color,
|
||||
'Colored link' : parse_template_Colored_link,
|
||||
|
||||
'@' : parse_template_At,
|
||||
'User link' : parse_template_User_link,
|
||||
|
||||
'Module:If empty' : parse_module_If_empty,
|
||||
|
||||
// 一些會添加 anchors 的特殊模板。
|
||||
// 會生成網頁錨點的模板或模組。
|
||||
// Templates or modules that generate web anchors
|
||||
Anchor : parse_template_Anchor,
|
||||
'Module:Anchor' : parse_template_Anchor,
|
||||
'Visible anchor' : parse_template_Visible_anchor,
|
||||
Term : parse_template_Term,
|
||||
Wikicite : parse_template_Wikicite,
|
||||
// Sfn : parse_template_Sfn,
|
||||
SfnRef : parse_template_SfnRef,
|
||||
'Episode table' : parse_template_Episode_table,
|
||||
'Episode table/part' : parse_template_Episode_table__part,
|
||||
'Episode list' : parse_template_Episode_list,
|
||||
'Episode list/sublist' : parse_template_Episode_list,
|
||||
|
||||
// wiki/routine/20210429.Auto-archiver.js: avoid being archived
|
||||
'Pin message' : parse_template_Pin_message,
|
||||
|
||||
'Module:Check for unknown parameters' : parse_module_Check_for_unknown_parameters,
|
||||
|
||||
'Module:IPAddress' : parse_module_IPAddress,
|
||||
|
||||
// TODO
|
||||
// 'Module:Unsubst' : parse_module_Unsubst,
|
||||
|
||||
// 'Template link'
|
||||
Tl : parse_template_Template_link
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不設定(hook)本 module 之 namespace,僅執行 module code。
|
||||
return library_namespace.env.not_to_extend_keyword;
|
||||
}
|
161
app/node_modules/cejs/application/net/wiki/template_functions/jawiki.js
generated
vendored
Normal file
161
app/node_modules/cejs/application/net/wiki/template_functions/jawiki.js
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科): 常用模板特設功能。本工具檔放置的是指定 wiki
|
||||
* 計畫特有的模板。
|
||||
*
|
||||
* 注意: 本程式庫必須應各 wiki project 模板內容改動而改寫。
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* TODO:<code>
|
||||
|
||||
</code>
|
||||
*
|
||||
* @since 2021/1/24 16:6:50
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.template_functions.jawiki',
|
||||
|
||||
require : 'data.native.'
|
||||
// Should also load essential MediaWiki modules
|
||||
+ '|application.net.wiki.',
|
||||
|
||||
// 設定不匯出的子函式。
|
||||
no_extend : 'this,*',
|
||||
|
||||
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
|
||||
code : module_code
|
||||
});
|
||||
|
||||
function module_code(library_namespace) {
|
||||
|
||||
// requiring
|
||||
var wiki_API = library_namespace.application.net.wiki;
|
||||
// @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;
|
||||
|
||||
// e.g., 'zhwiki'
|
||||
var module_site_name = this.id.match(/[^.]+$/)[0];
|
||||
|
||||
function empty_string(/* options */) {
|
||||
// var token = this;
|
||||
return '';
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// token.expand() 可將模板轉換成一般 wiki 語法。
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=expandtemplates
|
||||
// 用於 function preprocess_section_link_token()。
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_Enlink(options) {
|
||||
var parameters = this.parameters;
|
||||
var lang = parameters[3] || 'en';
|
||||
var wikitext;
|
||||
if (parameters.a === 'on') {
|
||||
wikitext = lang;
|
||||
} else {
|
||||
wikitext = (parameters[3] ? parameters[3] + ':' : '')
|
||||
+ (parameters[2] || parameters[1]);
|
||||
if (parameters.i === 'on')
|
||||
wikitext = "''" + wikitext + "''";
|
||||
}
|
||||
|
||||
wikitext = '[[:' + lang + ':' + parameters[1]
|
||||
//
|
||||
+ '|' + wikitext + ']]';
|
||||
|
||||
if (!parameters.p || parameters.p === 'on')
|
||||
wikitext = ' (' + wikitext + ' ';
|
||||
if (!parameters.s || parameters.s === 'on')
|
||||
wikitext = '<small>' + wikitext + '</small>';
|
||||
return ' (' + parameters[1] + ')';
|
||||
}
|
||||
|
||||
function parse_template_Enlink(token) {
|
||||
token.expand = expand_template_Enlink;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_to_display_language(options) {
|
||||
// console.trace(this.toString());
|
||||
var parameters = this.parameters;
|
||||
return parameters[1];
|
||||
}
|
||||
|
||||
function parse_template_to_display_language(token) {
|
||||
token.expand = expand_template_to_display_language;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_拡張漢字(options) {
|
||||
var parameters = this.parameters;
|
||||
return parameters[2] || parameters[1];
|
||||
}
|
||||
|
||||
function parse_template_拡張漢字(token) {
|
||||
token.expand = expand_template_拡張漢字;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// Not completed! Only for get_all_anchors().
|
||||
// 転送先のアンカーはTemplate:RFDの中に納まっている
|
||||
// e.g., {{RFD notice
|
||||
// |'''対象リダイレクト:'''[[Wikipedia:リダイレクトの削除依頼/受付#RFD長崎市電|長崎市電(受付依頼)]]|...}}
|
||||
function expand_template_RFD(options) {
|
||||
var parameters = this.parameters;
|
||||
// {{RFD|リダイレクト元ページ名|リダイレクト先ページ名}}
|
||||
return '<span id="RFD' + parameters[1] + '"></span>'
|
||||
// TODO: + ...
|
||||
;
|
||||
}
|
||||
|
||||
function parse_template_RFD(token) {
|
||||
token.expand = expand_template_RFD;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
wiki_API.template_functions.functions_of_site[module_site_name] = {
|
||||
// 一些會添加 anchors 的特殊模板。
|
||||
Anchors : wiki_API.template_functions.functions_of_all_sites.Anchor,
|
||||
|
||||
Enlink : parse_template_Enlink,
|
||||
|
||||
ARIB外字フォント : parse_template_to_display_language,
|
||||
CP932フォント : parse_template_to_display_language,
|
||||
JIS90フォント : parse_template_to_display_language,
|
||||
JIS2004フォント : parse_template_to_display_language,
|
||||
MacJapanese : parse_template_to_display_language,
|
||||
変体仮名フォント : parse_template_to_display_language,
|
||||
絵文字フォント : parse_template_to_display_language,
|
||||
補助漢字フォント : parse_template_to_display_language,
|
||||
通貨フォント : parse_template_to_display_language,
|
||||
拡張漢字 : parse_template_拡張漢字,
|
||||
|
||||
RFD : parse_template_RFD
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不設定(hook)本 module 之 namespace,僅執行 module code。
|
||||
return library_namespace.env.not_to_extend_keyword;
|
||||
}
|
210
app/node_modules/cejs/application/net/wiki/template_functions/zhmoegirl.js
generated
vendored
Normal file
210
app/node_modules/cejs/application/net/wiki/template_functions/zhmoegirl.js
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科): 常用模板特設功能。本工具檔放置的是指定 wiki
|
||||
* 計畫特有的模板。
|
||||
*
|
||||
* 注意: 本程式庫必須應各 wiki project 模板內容改動而改寫。
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* TODO:<code>
|
||||
|
||||
</code>
|
||||
*
|
||||
* @since 2021/1/24 16:6:50
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.template_functions.zhmoegirl',
|
||||
|
||||
require : 'data.native.'
|
||||
// Should also load essential MediaWiki modules
|
||||
+ '|application.net.wiki.',
|
||||
|
||||
// 設定不匯出的子函式。
|
||||
no_extend : 'this,*',
|
||||
|
||||
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
|
||||
code : module_code
|
||||
});
|
||||
|
||||
function module_code(library_namespace) {
|
||||
|
||||
// requiring
|
||||
var wiki_API = library_namespace.application.net.wiki;
|
||||
// @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;
|
||||
|
||||
// e.g., 'zhwiki'
|
||||
var module_site_name = this.id.match(/[^.]+$/)[0];
|
||||
|
||||
function empty_string(/* options */) {
|
||||
// var token = this;
|
||||
return '';
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// token.expand() 可將模板轉換成一般 wiki 語法。
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=expandtemplates
|
||||
// 用於 function preprocess_section_link_token()。
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// for get_all_anchors()
|
||||
function expand_template_A(options) {
|
||||
var parameters = this.parameters;
|
||||
// {{a|锚点名称|显示文字}}
|
||||
return '<span id="' + parameters[1] + '">'
|
||||
+ (parameters[2] || parameters[1]) + '</span>';
|
||||
}
|
||||
|
||||
function parse_template_A(token, index, parent, options) {
|
||||
token.expand = expand_template_A;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// [[Module:Ruby]]
|
||||
function expand_module_Ruby(parameters) {
|
||||
// converted wikitext
|
||||
var wikitext = [];
|
||||
wikitext.push('<ruby'
|
||||
+ (parameters.id ? ' id="' + parameters.id + '"' : '') + '>');
|
||||
wikitext.push('<rb'
|
||||
+ (parameters.rbid ? ' id="' + parameters.rbid + '"' : '')
|
||||
+ '>' + (parameters[1] || '') + '</rb>');
|
||||
wikitext.push('(');
|
||||
wikitext.push('<rt'
|
||||
+ (parameters.rtid ? ' id="' + parameters.rtid + '"' : '')
|
||||
+ '>' + (parameters[2] || '') + '</rt>');
|
||||
wikitext.push(')');
|
||||
wikitext.push('</ruby>');
|
||||
return wikitext.join('');
|
||||
}
|
||||
|
||||
// for get_all_anchors()
|
||||
function expand_template_Ruby(options) {
|
||||
var parameters = this.parameters;
|
||||
// {{Ruby|文字|注音|文字的語言标签|注音的語言标签}}
|
||||
return expand_module_Ruby(parameters);
|
||||
}
|
||||
|
||||
function parse_template_Ruby(token, index, parent, options) {
|
||||
token.expand = expand_template_Ruby;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// for preprocess_section_link_token()
|
||||
function expand_template_Dead(options) {
|
||||
var parameters = this.parameters;
|
||||
return parameters[1];
|
||||
}
|
||||
|
||||
function parse_template_Dead(token) {
|
||||
token.expand = expand_template_Dead;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// for preprocess_section_link_token()
|
||||
function expand_template_黑幕(options) {
|
||||
var parameters = this.parameters;
|
||||
return parameters[1];
|
||||
}
|
||||
|
||||
function parse_template_黑幕(token) {
|
||||
token.expand = expand_template_黑幕;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// for preprocess_section_link_token()
|
||||
// {{Lj|...}} 是日語{{lang|ja|...}}的縮寫 @ zh.moegirl
|
||||
function expand_template_Lj(options) {
|
||||
var parameters = this.parameters;
|
||||
return '-{' + parameters[1] + '}-';
|
||||
}
|
||||
|
||||
function parse_template_Lj(token) {
|
||||
token.expand = expand_template_Lj;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// Not completed! Only for get_all_anchors() @ [[ACGN作品中出場的鐵路車站列表]]
|
||||
function expand_template_铁路车站名(options) {
|
||||
var parameters = this.parameters;
|
||||
return '<span id="' + (parameters.name || parameters[1]) + '">'
|
||||
// TODO: The content is skipped.
|
||||
+ '</span>';
|
||||
}
|
||||
|
||||
function parse_template_铁路车站名(token) {
|
||||
token.expand = expand_template_铁路车站名;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// Not completed! Only for get_all_anchors() as section title
|
||||
// @ [[ARGONAVIS from BanG Dream! 翻唱曲列表]]
|
||||
function expand_template_ARGONAVIS_Icon(options) {
|
||||
// TODO: The content is skipped.
|
||||
return '';
|
||||
}
|
||||
|
||||
function parse_template_ARGONAVIS_Icon(token) {
|
||||
token.expand = expand_template_ARGONAVIS_Icon;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// Not completed! Only for get_all_anchors()
|
||||
// @ zh.moegirl [[FLOWERS(Innocent Grey)]]
|
||||
function expand_template_Gradient_Text(options) {
|
||||
var parameters = this.parameters;
|
||||
// {{Gradient_Text|漸變色代碼|文字內容|title=鼠標懸停在文字上顯示的注釋}}
|
||||
return parameters[2] || '';
|
||||
}
|
||||
|
||||
function parse_template_Gradient_Text(token) {
|
||||
token.expand = expand_template_Gradient_Text;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
wiki_API.template_functions.functions_of_site[module_site_name] = {
|
||||
// 一些會添加 anchors 的特殊模板。
|
||||
A : parse_template_A,
|
||||
Ruby : parse_template_Ruby,
|
||||
铁路车站名 : parse_template_铁路车站名,
|
||||
|
||||
// 一些會用於章節標題的特殊模板。 for preprocess_section_link_token()
|
||||
Dead : parse_template_Dead,
|
||||
黑幕 : parse_template_黑幕,
|
||||
Lj : parse_template_Lj,
|
||||
'ARGONAVIS/Icon' : parse_template_ARGONAVIS_Icon,
|
||||
'Gradient Text' : parse_template_Gradient_Text
|
||||
};
|
||||
|
||||
// library_namespace.info(module_site_name + ': 採用 zhwiki 的模板特設功能設定。');
|
||||
wiki_API.template_functions.functions_of_site[module_site_name][wiki_API.template_functions.KEY_dependent_on] = [ 'zhwiki' ];
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不設定(hook)本 module 之 namespace,僅執行 module code。
|
||||
return library_namespace.env.not_to_extend_keyword;
|
||||
}
|
387
app/node_modules/cejs/application/net/wiki/template_functions/zhwiki.js
generated
vendored
Normal file
387
app/node_modules/cejs/application/net/wiki/template_functions/zhwiki.js
generated
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科): 常用模板特設功能。本工具檔放置的是指定 wiki
|
||||
* 計畫特有的模板。
|
||||
*
|
||||
* 注意: 本程式庫必須應各 wiki project 模板內容改動而改寫。
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* TODO:<code>
|
||||
|
||||
</code>
|
||||
*
|
||||
* @since 2021/1/24 16:6:50
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// @examples
|
||||
(function() {
|
||||
require('./wiki loader.js');
|
||||
CeL.run('application.net.wiki.template_functions');
|
||||
var wiki = Wiki(true, 'zh');
|
||||
wiki.page('簡繁轉換一對多列表').parse(function(parsed) {
|
||||
// var page_data = parsed.page;
|
||||
parsed.each('Template:簡繁轉換', function(token) {
|
||||
console.log(token.简 + '⇄' + token.繁);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.template_functions.zhwiki',
|
||||
|
||||
require : 'data.native.'
|
||||
// Should also load essential MediaWiki modules
|
||||
+ '|application.net.wiki.',
|
||||
|
||||
// 設定不匯出的子函式。
|
||||
no_extend : 'this,*',
|
||||
|
||||
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
|
||||
code : module_code
|
||||
});
|
||||
|
||||
function module_code(library_namespace) {
|
||||
|
||||
// requiring
|
||||
var wiki_API = library_namespace.application.net.wiki;
|
||||
// @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;
|
||||
|
||||
// e.g., 'zhwiki'
|
||||
var module_site_name = this.id.match(/[^.]+$/)[0];
|
||||
|
||||
function empty_string(/* options */) {
|
||||
// var token = this;
|
||||
return '';
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// token.expand() 可將模板轉換成一般 wiki 語法。
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=expandtemplates
|
||||
// 用於 function preprocess_section_link_token()。
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_A(options) {
|
||||
var parameters = this.parameters;
|
||||
return (parameters.name ? '<span id="' + parameters.name
|
||||
//
|
||||
+ '"></span>' : '') + '[[' + parameters[1]
|
||||
//
|
||||
+ (parameters[2] ? '|' + parameters[2] : '') + ']]';
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// [[w:zh:Template:Al]]
|
||||
function expand_template_Al(options) {
|
||||
var token = this;
|
||||
return token.page_title_list.map(function(title) {
|
||||
return wiki_API.title_link_of(title);
|
||||
}).join('、');
|
||||
}
|
||||
|
||||
function parse_template_Al(token, index, parent, options) {
|
||||
var index = 0, page_title_list = [];
|
||||
while (index < token.length) {
|
||||
var page_title = token.parameters[++index];
|
||||
// allow `{{al||title}}`
|
||||
if (page_title)
|
||||
page_title_list.push(page_title);
|
||||
}
|
||||
|
||||
Object.assign(token, {
|
||||
page_title_list : page_title_list,
|
||||
expand : expand_template_Al
|
||||
});
|
||||
return page_title_list;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function parse_template_不存檔(token, index, parent, options) {
|
||||
token.message_expire_date = Infinity;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_楷體(options) {
|
||||
var parameters = this.parameters;
|
||||
return '<span class="template-kai">' + (parameters[1] || '楷体')
|
||||
+ '</span>';
|
||||
}
|
||||
|
||||
function parse_template_楷體(token, index, parent, options) {
|
||||
token.expand = expand_template_楷體;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* [[Template:Interlanguage link]] 跨語言模板 多語言模板。會為 token 增加下列屬性: <code>
|
||||
|
||||
</code>
|
||||
*/
|
||||
var interlanguage_link_template_attributes = {
|
||||
// local_title: local title 中文條目名
|
||||
"local_page_title" : '',
|
||||
// 只會提供第一個。
|
||||
"foreign_language_code" : '',
|
||||
// 只會提供第一個。
|
||||
"foreign_page_title" : '',
|
||||
"foreign_page_mapper" : {
|
||||
// foreign_language: foreign language code 外文語言代號
|
||||
// foreign_title: foreign title 外文條目名
|
||||
foreign_language_code : 'foreign_page_title'
|
||||
},
|
||||
|
||||
// label: label text displayed 顯示名
|
||||
"display_text" : '',
|
||||
// Keep foreign language links when displayed
|
||||
"preserve_foreign_links" : true,
|
||||
"wikidata_entity_id" : '' || 1,
|
||||
|
||||
// 屬性的index,改變屬性值時使用。
|
||||
"attribute_index" : {
|
||||
"local_page_title" : 1,
|
||||
// 只會提供第一個。
|
||||
"foreign_language_code" : 1,
|
||||
// 只會提供第一個。
|
||||
"foreign_page_title" : 1,
|
||||
"display_text" : 1,
|
||||
"preserve_foreign_links" : 1,
|
||||
"wikidata_entity_id" : 1
|
||||
}
|
||||
};
|
||||
|
||||
function setup_interlanguage_link_template_parameters(template_pattern) {
|
||||
var parsed_token = wiki_API.parse(template_pattern);
|
||||
var attribute_index = Object.create(null);
|
||||
var configuration = {
|
||||
attribute_index : attribute_index
|
||||
};
|
||||
var parameters = parsed_token.parameters;
|
||||
|
||||
for ( var parameter in parameters) {
|
||||
var attribute_name = parameters[parameter];
|
||||
if (attribute_name in interlanguage_link_template_attributes)
|
||||
attribute_index[attribute_name] = parameter;
|
||||
}
|
||||
|
||||
var functions_of_site = wiki_API.template_functions.functions_of_site[module_site_name];
|
||||
if (functions_of_site[parsed_token.name]) {
|
||||
library_namespace
|
||||
.error('setup_interlanguage_link_template_parameters: '
|
||||
+ '已設定' + parsed_token.name
|
||||
+ '之模板特設功能,無法設定跨語言模板功能。');
|
||||
return;
|
||||
}
|
||||
functions_of_site[parsed_token.name] = parse_interlanguage_link_template
|
||||
.bind(configuration);
|
||||
}
|
||||
|
||||
function parse_interlanguage_link_template(token, index, parent, options) {
|
||||
var configuration = this;
|
||||
var attribute_index = configuration.attribute_index;
|
||||
var foreign_page_mapper = Object.create(null);
|
||||
|
||||
for ( var attribute_name in attribute_index) {
|
||||
if (attribute_index[attribute_name] in token.parameters)
|
||||
token[attribute_name] = token.parameters[attribute_index[attribute_name]];
|
||||
}
|
||||
|
||||
if ('foreign_language_code' in token)
|
||||
foreign_page_mapper[token.foreign_language_code] = token.foreign_page_title;
|
||||
|
||||
token.attribute_index = attribute_index;
|
||||
token.foreign_page_mapper = foreign_page_mapper;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// {{Lang|ja|參數值}} → -{參數值}-
|
||||
function expand_template_Lang(options) {
|
||||
var parameters = this.parameters;
|
||||
return /^(?:zh|gan)/.test(parameters[1]) ? parameters[2] : '-{'
|
||||
+ parameters[2] + '}-';
|
||||
}
|
||||
|
||||
function parse_template_Lang(token, index, parent, options) {
|
||||
token.expand = expand_template_Lang;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// [[w:zh:Template:NoteTA]]
|
||||
function parse_template_NoteTA(token, options) {
|
||||
var conversion_list = Object.assign([], {
|
||||
// 固定轉換規則
|
||||
// fixed : [],
|
||||
|
||||
// 公共轉換組
|
||||
group_data : [],
|
||||
groups : []
|
||||
});
|
||||
|
||||
var index, value = token.parameters.T;
|
||||
if (value) {
|
||||
// 標題轉換
|
||||
conversion_list.title = value;
|
||||
}
|
||||
|
||||
// TODO: {{NoteTA}} 使用「1=」可以同時轉換標題和正文(T=)?
|
||||
for (index = 1; index < token.length; index++) {
|
||||
value = token.parameters[index];
|
||||
if (!value)
|
||||
continue;
|
||||
// [[w:zh:模組:NoteTA]]
|
||||
// @see function item_to_conversion(item) @
|
||||
// CeL.application.net.wiki
|
||||
value = wiki_API.parse('-{A|' + value + '}-', {
|
||||
normalize : true,
|
||||
with_properties : true
|
||||
});
|
||||
if (typeof value === 'string') {
|
||||
// 遇到無法轉換的值別 throw。 e.g., "a\nb"
|
||||
continue;
|
||||
}
|
||||
// value.parameter_name = index;
|
||||
value.index = token.index_of[index];
|
||||
// console.log(value);
|
||||
conversion_list.push(value);
|
||||
}
|
||||
|
||||
// [[w:zh:Module:NoteTA]]
|
||||
for (index = 1; index < token.length; index++) {
|
||||
var parameter_name = 'G' + index;
|
||||
value = token.parameters[parameter_name];
|
||||
if (!value)
|
||||
continue;
|
||||
value = wiki_API.parse.wiki_token_to_key(value);
|
||||
// console.trace(value);
|
||||
if (typeof value === 'string') {
|
||||
value = value.replace(/_/g, ' ').trim();
|
||||
} else {
|
||||
library_namespace.warn('parse_template_NoteTA: 非字串之公共轉換組名稱: ['
|
||||
+ value + '] @ ' + token);
|
||||
console.trace(value);
|
||||
}
|
||||
conversion_list.groups.push(value.toString());
|
||||
conversion_list.group_data[value.toString()] = {
|
||||
parameter_name : parameter_name,
|
||||
group_name : value,
|
||||
index : token.index_of[parameter_name]
|
||||
};
|
||||
// TODO
|
||||
}
|
||||
|
||||
Object.assign(token, {
|
||||
conversion_list : conversion_list,
|
||||
expand : empty_string
|
||||
});
|
||||
return conversion_list;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function template_簡繁轉換_to_string(template_token, parameter) {
|
||||
var words = template_token.parameters[parameter];
|
||||
if (Array.isArray(words)) {
|
||||
words = words.map(function(token) {
|
||||
if (typeof token === 'string')
|
||||
return token;
|
||||
if (token.tag === 'sup') {
|
||||
// e.g., "<sup>台/陸繁</sup>"
|
||||
return '';
|
||||
}
|
||||
if (token.type === 'transclusion') {
|
||||
if (token.name === 'Lang'
|
||||
//
|
||||
&& typeof token.parameters[2] === 'string')
|
||||
return token.parameters[2];
|
||||
if (token.name === '僻字') {
|
||||
// console.log(token.toString());
|
||||
}
|
||||
if (token.name === '僻字'
|
||||
//
|
||||
&& typeof token.parameters[1] === 'string')
|
||||
return token.parameters[1];
|
||||
}
|
||||
throw new Error('包含無法處理的字元: ' + token);
|
||||
}).join('');
|
||||
}
|
||||
words = library_namespace.HTML_to_Unicode(words);
|
||||
// [[w:zh:Unicode字符平面映射]]
|
||||
// http://ubuntu-rubyonrails.blogspot.com/2009/06/unicode.html
|
||||
|
||||
words = words.replace(
|
||||
// 發音用 Pinyin diacritic-vowel combinations:
|
||||
// \u00E0-\u00FC [[w:en:Latin-1 Supplement (Unicode block)]]
|
||||
// \u0100-\u017F [[w:en:Latin Extended-A]]
|
||||
// \u01CD-\u01DC [[w:en:Latin Extended-B]]
|
||||
/[(),、a-z\u00E0-\u00FC\u0100-\u017F\u01CD-\u01DC\uD800-\uDFFF]/g, '');
|
||||
if (false && /[^\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u2E80-\u2EFF]/
|
||||
.test(words)) {
|
||||
// words.charCodeAt(0).toString(16)
|
||||
console.log([ words, words.replace(
|
||||
// 匹配中文字符的正則表達式
|
||||
/[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF\u2E80-\u2EFF]/g,
|
||||
//
|
||||
'') ]);
|
||||
// throw words;
|
||||
}
|
||||
return words;
|
||||
}
|
||||
|
||||
// for {{簡繁轉換}} @ [[w:zh:簡繁轉換一對多列表]]
|
||||
// @see wiki_API.convert_Chinese()
|
||||
function parse_template_簡繁轉換(token) {
|
||||
Object.assign(token, {
|
||||
简 : template_簡繁轉換_to_string(token, 's'),
|
||||
繁 : template_簡繁轉換_to_string(token, 't')
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
wiki_API.template_functions.functions_of_site[module_site_name] = {
|
||||
// 一些會用於章節標題的特殊模板。 for preprocess_section_link_token()
|
||||
A : {
|
||||
properties : {
|
||||
expand : expand_template_A
|
||||
}
|
||||
},
|
||||
Al : parse_template_Al,
|
||||
|
||||
// {{Do not archive}}
|
||||
// wiki/routine/20210429.Auto-archiver.js: avoid being archived
|
||||
不存檔 : parse_template_不存檔,
|
||||
|
||||
// [[Template:Interlanguage link]] 跨語言模板 多語言模板。
|
||||
|
||||
Lang : parse_template_Lang,
|
||||
NoteTA : parse_template_NoteTA,
|
||||
簡繁轉換 : parse_template_簡繁轉換
|
||||
};
|
||||
|
||||
[ '{{Interlanguage link multi|local_page_title|foreign_language_code|foreign_page_title|lt=display_text|WD=wikidata_entity_id}}' ]
|
||||
.forEach(setup_interlanguage_link_template_parameters);
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不設定(hook)本 module 之 namespace,僅執行 module code。
|
||||
return library_namespace.env.not_to_extend_keyword;
|
||||
}
|
85
app/node_modules/cejs/application/net/wiki/template_functions/zhwiktionary.js
generated
vendored
Normal file
85
app/node_modules/cejs/application/net/wiki/template_functions/zhwiktionary.js
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @name CeL function for MediaWiki (Wikipedia / 維基百科): 常用模板特設功能。本工具檔放置的是指定 wiki
|
||||
* 計畫特有的模板。
|
||||
*
|
||||
* 注意: 本程式庫必須應各 wiki project 模板內容改動而改寫。
|
||||
*
|
||||
* @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。
|
||||
*
|
||||
* TODO:<code>
|
||||
|
||||
</code>
|
||||
*
|
||||
* @since 2021/11/19 5:4:8
|
||||
*/
|
||||
|
||||
// More examples: see /_test suite/test.js
|
||||
// Wikipedia bots demo: https://github.com/kanasimi/wikibot
|
||||
'use strict';
|
||||
// 'use asm';
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||||
typeof CeL === 'function' && CeL.run({
|
||||
// module name
|
||||
name : 'application.net.wiki.template_functions.zhwiktionary',
|
||||
|
||||
require : 'data.native.'
|
||||
// Should also load essential MediaWiki modules
|
||||
+ '|application.net.wiki.',
|
||||
|
||||
// 設定不匯出的子函式。
|
||||
no_extend : 'this,*',
|
||||
|
||||
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
|
||||
code : module_code
|
||||
});
|
||||
|
||||
function module_code(library_namespace) {
|
||||
|
||||
// requiring
|
||||
var wiki_API = library_namespace.application.net.wiki;
|
||||
// @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;
|
||||
|
||||
// e.g., 'zhwiktionary'
|
||||
var module_site_name = this.id.match(/[^.]+$/)[0];
|
||||
|
||||
function empty_string(/* options */) {
|
||||
// var token = this;
|
||||
return '';
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// token.expand() 可將模板轉換成一般 wiki 語法。
|
||||
// https://www.mediawiki.org/w/api.php?action=help&modules=expandtemplates
|
||||
// 用於 function preprocess_section_link_token()。
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
function expand_template_語言標題(options) {
|
||||
var parameters = this.parameters;
|
||||
return '\n==' + (parameters.l || parameters.語 || parameters.语) + '==';
|
||||
}
|
||||
|
||||
function parse_template_語言標題(token, index, parent, options) {
|
||||
token.expand = expand_template_語言標題;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// export 導出.
|
||||
|
||||
wiki_API.template_functions.functions_of_site[module_site_name] = {
|
||||
語言標題 : parse_template_語言標題,
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
// 不設定(hook)本 module 之 namespace,僅執行 module code。
|
||||
return library_namespace.env.not_to_extend_keyword;
|
||||
}
|
Reference in New Issue
Block a user