cejs
Version:
A JavaScript module framework that is simple to use.
1,594 lines (1,445 loc) • 144 kB
JavaScript
/**
* @name CeL function for MediaWiki (Wikipedia / 維基百科): basic 工具函數, namespace,
* site configuration
*
* @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.namespace',
require : 'data.native.'
// for library_namespace.get_URL
// + '|application.net.Ajax.'
// for library_namespace.URI()
+ '|application.net.'
// CeL.DOM.HTML_to_Unicode(), CeL.DOM.Unicode_to_HTML()
+ '|interact.DOM.'
// setup module namespace
+ '|application.net.wiki.',
// 設定不匯出的子函式。
no_extend : '*',
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
code : module_code
});
function module_code(library_namespace) {
// requiring
var wiki_API = library_namespace.application.net.wiki;
var gettext = library_namespace.cache_gettext(function(_) {
gettext = _;
});
// --------------------------------------------------------------------------------------------
wiki_API.general_parameters = {
format : 'json',
// https://www.mediawiki.org/w/api.php?action=help&modules=json
// 加上 "&utf8", "&utf8=1" 可能會導致把某些 link 中 URL 編碼也給 unescape 的情況!
utf8 : 1
};
// 設定可被匯入 general_parameters 的屬性。
wiki_API.general_parameters_normalizer = {
// for cross-domain AJAX request (CORS)
origin : function(value) {
if (value === true)
value = '*';
return value;
},
format : 'string',
utf8 : 'boolean|number|string'
};
// CeL.wiki.KEY_SESSION
/** {String}KEY_wiki_session old key: 'wiki' */
var KEY_SESSION = 'session', KEY_HOST_SESSION = 'host';
// @inner TODO: MUST re-design
function get_wikimedia_project_name(session) {
return wiki_API.is_wiki_API(session)
// e.g., commons, wikidata
&& session.family === 'wikimedia'
// https://meta.wikimedia.org/wiki/Special:SiteMatrix
// TODO: using session.project_name or something others
// using get_first_domain_name_of_session()
&& session.language;
}
// https://meta.wikimedia.org/wiki/Help:Page_name#Special_characters
// @see $wgLegalTitleChars
var PATTERN_invalid_page_name_characters = /[{}\[\]\|<>\t\n#�]/,
// https://en.wikipedia.org/wiki/Wikipedia:Naming_conventions_(technical_restrictions)#Forbidden_characters
PATTERN_page_name = /((?:&#(?:\d{1,8}|x[\da-fA-F]{1,8});|[^{}\[\]\|<>\t\n#�])+)/,
/**
* {RegExp}wikilink內部連結的匹配模式v2 [ all_link, page_and_anchor, page_name,
* anchor / section_title, pipe_separator, displayed_text ]
*
* 頁面標題不可包含無效的字元:PATTERN_invalid_page_name_characters,<br />
* 經測試 anchor 亦不可包含[\n\[\]{}],但 display text 表達文字可以包含 [\n]
*
* @see PATTERN_link
*/
PATTERN_wikilink = /\[\[(((?:&#(?:\d{1,8}|x[\da-fA-F]{1,8});|[^{}\[\]\|<>\t\n#�])+)(#(?:-{[^\[\]{}\t\n\|]+}-|[^\[\]{}\t\n\|]+)?)?|#[^\[\]{}\t\n\|]*)(?:(\||{{\s*!\s*}})([\s\S]+?))?\]\]/,
//
PATTERN_wikilink_global = new RegExp(PATTERN_wikilink.source, 'g');
var
/**
* 匹配URL網址。
*
* [http://...]<br />
* {{|url=http://...}}
*
* matched: [ URL ]
*
* TODO: "https://[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443/",
* "{{t0|urn:0{{t1|urn:1}}}}"
*
* @deprecated Use `PATTERN_URL_WITH_PROTOCOL_GLOBAL` instead.
*
* @see https://github.com/5j9/wikitextparser/blob/master/tests/wikitext/test_external_links.py
* https://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers
*
* @type {RegExp}
*
* @see PATTERN_URL_GLOBAL, PATTERN_URL_WITH_PROTOCOL_GLOBAL,
* PATTERN_URL_prefix, PATTERN_WIKI_URL, PATTERN_wiki_project_URL,
* PATTERN_external_link_global
*/
// PATTERN_URL_GLOBAL = /(?:https?:)?\/\/(?:[^\s\|<>\[\]{}]+|{[^{}]*})+/ig,
/**
* 匹配URL網址,僅用於 parse_wikitext()。
*
* matched: [ all, previous, URL, protocol with "://", URL_others ]
*
* @type {RegExp}
*
* @see https://en.wikipedia.org/wiki/Help:URL#Fixing_links_with_unsupported_characters
* https://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers
*
* @see PATTERN_URL_GLOBAL, PATTERN_URL_WITH_PROTOCOL_GLOBAL,
* PATTERN_URL_prefix, PATTERN_WIKI_URL, PATTERN_wiki_project_URL,
* PATTERN_external_link_global
*/
PATTERN_URL_WITH_PROTOCOL_GLOBAL
// 警告: PATTERN_external_link_global 會用到 '):)'
= /(^|[^a-z\d_])(((?:https?|ssh|telnet|ftps?|sftp|gopher|ircs?|news|nntp|worldwind|svn|git|mms):?\/\/|(?:mailto|urn):)((?:\[[a-f\d:]+\]|[^\s\|<>\[\]\/])[^\s\|<>\[\]]*))/ig;
/**
* 匹配以URL網址起始。
*
* matched: [ prefix ]
*
* @type {RegExp}
*
* @see PATTERN_URL_GLOBAL, PATTERN_URL_WITH_PROTOCOL_GLOBAL,
* PATTERN_URL_prefix, PATTERN_WIKI_URL, PATTERN_wiki_project_URL,
* PATTERN_external_link_global
*/
var PATTERN_URL_prefix = new RegExp(PATTERN_URL_WITH_PROTOCOL_GLOBAL.source
.replace(/^\([^()]+\)/, '(^)'), 'i');
// 嘗試從 options 取得 API_URL。
function API_URL_of_options(options) {
// library_namespace.debug('options:', 0, 'API_URL_of_options');
// console.log(options);
var session = session_of_options(options);
if (session) {
return session.API_URL;
}
}
/**
* 測試看看指定值是否為API語言以及頁面標題或者頁面。
*
* @param value
* value to test. 要測試的值。
* @param {Boolean|String}[type]
* test type: true('simple'), 'language', 'URL'
* @param {Object}[options]
* 附加參數/設定選擇性/特殊功能與選項
*
* @returns {Boolean}value 為 [ {String}API_URL/language, {String}title or
* {Object}page_data ]
*/
function is_api_and_title(value, type, options) {
// console.trace(value);
if (!Array.isArray(value) || value.length !== 2
//
|| get_page_content.is_page_data(value[0])) {
// 若有必要設定,應使用 wiki_API.normalize_title_parameter(title, options)。
// 此時不能改變傳入之 value 本身,亦不能僅測試是否有 API_URL。
return false;
}
var API_URL = value[0];
/** {Boolean|String} ignore API test, 'set': set API */
var ignore_API_test;
if (typeof options === 'object') {
ignore_API_test = options.ignore_API_test;
} else {
ignore_API_test = options;
options = null;
}
if (type === true) {
// type === true: simple test, do not test more.
return !API_URL || typeof API_URL === 'string';
}
var title = value[1];
// test title: {String}title or {Object}page_data or {Array}titles
if (!title || typeof title !== 'string'
// value[1] 為 titles (page list)。
&& !Array.isArray(title)
// 為了預防輸入的是問題頁面。
&& !get_page_content.is_page_data(title)
// 處理 is_id。
&& (!(title > 0)
// 注意:這情況下即使是{Natural}page_id 也會pass!
|| !options || !options.is_id)) {
library_namespace.debug('輸入的是問題頁面 title: ' + title, 2,
'is_api_and_title');
return false;
}
// test API_URL: {String}API_URL/language
if (!API_URL) {
if (options) {
library_namespace.debug('嘗試從 options[KEY_SESSION] 取得 API_URL。',
2, 'is_api_and_title');
// console.log(options);
// console.log(API_URL_of_options(options));
API_URL = API_URL_of_options(options);
if (API_URL) {
value[0] = API_URL;
}
// 接下來繼續檢查 API_URL。
} else {
return !!ignore_API_test;
}
}
if (typeof API_URL !== 'string') {
// 若是未設定 action[0],則將在 wiki_API.query() 補設定。
// 因此若為 undefined || null,此處先不回傳錯誤。
return !API_URL;
}
// for property = [ {String}language, {String}title or {Array}titles ]
if (type === 'language') {
var metched = PATTERN_PROJECT_CODE_i.test(API_URL);
if (options && options.multi === false) {
// 明確指定 `title` 為單一標題,{Array} 只能解釋為 [ language, title ]。
if (!metched) {
library_namespace.warn('is_api_and_title: 強制將 "' + API_URL
+ '" 視為語言代碼 [' + value + ']');
}
return true;
}
return metched;
}
// 處理 [ {String}API_URL/language, {String}title or {Object}page_data ]
var metched = PATTERN_URL_prefix.test(API_URL);
if (type === 'URL') {
return metched;
}
// for key = [ {String}language, {String}title or {Array}titles ]
// for id = [ {String}language/site, {String}title ]
return metched || PATTERN_PROJECT_CODE_i.test(API_URL);
}
/**
* 規範化 title_parameter
*
* setup [ {String}API_URL, title ]
*
* @param {String|Array}title
* @param {Object}[options]
* 附加參數/設定選擇性/特殊功能與選項
*
* @returns {Array}action = [ {String}API_URL, {Search_parameters}parameters ]
*
* @see api_URL
*/
function normalize_title_parameter(title, options) {
options = library_namespace.setup_options(options);
var session = wiki_API.session_of_options(options);
if (false && library_namespace.is_Set(title)) {
title = Array.from(title);
}
var action = options.multi && Array.isArray(title)
&& title.length === 2
// 即便設定 options.multi,也不該有 /^https?:\/\/.+\.php/i 的標題。
&& !/^https?:\/\/.+\.php$/.test(title[0])
|| !is_api_and_title(title, true) ? [
session && session.API_URL || undefined, title ]
// title.clone(): 不改變原 title。
: Array.isArray(title) ? title.clone() : [];
if (false && library_namespace.is_Set(action[1])) {
action[1] = Array.from(action[1]);
}
if (options.slice_size >= 1) {
// console.trace(action);
if (Array.isArray(action[1])) {
if (action[1].length > options.slice_size) {
var titles_left = action[1].splice(options.slice_size,
action[1].length);
if (Array.isArray(options.titles_left)) {
Array.prototype.unshift.apply(options.titles_left,
titles_left);
} else {
if (options.titles_left) {
throw new Error(
// Warning:
'normalize_title_parameter: Invalid usage: options.titles_left is not {Array}!');
titles_left.push(options.titles_left);
}
options.titles_left = titles_left;
library_namespace
.warn('normalize_title_parameter: 將 title list 切分成 slice: '
+ action[1].length
+ ' + '
+ options.titles_left.length + '。');
}
}
} else if (!action[1] && Array.isArray(options.titles_left)) {
action[1] = options.titles_left.splice(0, options.slice_size);
library_namespace
.log('normalize_title_parameter: 接續取得 title list slice: '
+ action[1].length
+ ' + '
+ options.titles_left.length + '。');
}
}
if (Array.isArray(options.titles_left)
&& options.titles_left.length === 0) {
delete options.titles_left;
}
// console.trace([ title, action ]);
if (!is_api_and_title(action, false, options)) {
// console.trace('normalize_title_parameter: Invalid title!');
library_namespace.warn([ 'normalize_title_parameter: ', {
// gettext_config:{"id":"invalid-title-$1"}
T : [ 'Invalid title: %1',
//
wiki_API.title_link_of(title)
//
|| '(title: ' + JSON.stringify(title) + ')' ]
} ]);
// console.trace(JSON.stringify(title));
return;
}
// 處理 [ {String}API_URL, title ]
action[1] = wiki_API.query.title_param(action[1],
//
'multi_param' in options ? options.multi_param
// 'multi' in options
: options.multi !== undefined ? options.multi : true, options.is_id);
if (options.redirects) {
// 舊版毋須 '&redirects=1','&redirects' 即可。
action[1].redirects = 1;
}
// console.trace(action);
return action;
}
/**
* set / append additional parameters of MediaWiki API.
*
* @param {Array}action
* @param {Object}options
* 附加參數/設定選擇性/特殊功能與選項
* @inner
*/
function set_parameters(action, options) {
if (!options.parameters) {
return;
}
// Should use
// `action = wiki_API.extract_parameters(options, action, true);`
if (typeof action[1] === 'string' && !/^[a-z]+=/.test(action[1])) {
library_namespace
.warn('set_parameters: Did not set action! Will auto add "action=".');
console.trace(action);
action[1] = 'action=' + action[1];
}
// action[1] =
// wiki_API.extract_parameters(options.parameters, action[1], true);
action[1] = library_namespace.Search_parameters(action[1]);
action[1].set_parameters(options.parameters);
}
// --------------------------------------------------------------------------------------------
// 工具函數。
// https://phabricator.wikimedia.org/rOPUP558bcc29adc3dd7dfebbc66c1bf88a54a8b09535#3ce6dc61
// server:
// (wikipedia|wikibooks|wikinews|wikiquote|wikisource|wikiversity|wikivoyage|wikidata|wikimediafoundation|wiktionary|mediawiki)
// e.g., [[s:]], [[zh-classical:]], [[zh-min-nan:]], [[test2:]],
// [[metawikipedia:]], [[betawikiversity:]]
// @see [[m:Help:Interwiki linking#Project titles and shortcuts]],
// [[:zh:Help:跨语言链接#出現在正文中的連結]]
// https://www.wikidata.org/w/api.php?action=help&modules=wbsearchentities
// 警告: 應配合 get_namespace.pattern 排除 'Talk', 'User', 'Help', 'File', ...
var PATTERN_PROJECT_CODE = /^[a-z][a-z\d\-]{0,14}$/,
// 須亦能匹配 site key:
// https://www.wikidata.org/w/api.php?action=help&modules=wbgetentities
PATTERN_PROJECT_CODE_i = new RegExp(PATTERN_PROJECT_CODE.source, 'i');
// 可用來拆分 language, family。以防 incase wikt, wikisource
// testwikidatawiki → [,testwikidata,wiki]
// https://www.wikidata.org/w/api.php?action=help&modules=wbsearchentities
// e.g., 'zh_min_nanwikibooks'
// MariaDB [zhwiki_p]> SHOW DATABASES;
// e.g., "wikimania2018wiki_p"
// 2020/5 left:
// ["centralauth_p","heartbeat_p","information_schema","information_schema_p","meta_p"]
// [ all, language code, family ]
var PATTERN_SITE = /^([a-z\d\_]{2,13})(wiki|wikibooks|wiktionary|wikiquote|wikisource|wikinews|wikiversity|wikivoyage|wikimedia)$/;
/**
* Wikimedia projects 的 URL match pattern 匹配模式。
*
* matched: [ 0: protocol + host name, 1: protocol, 2: host name,<br />
* 3: 第一 domain name (e.g., language code / project),<br />
* 4: 第二 domain name (e.g., family: 'wikipedia') ]
*
* @deprecated using wiki_API.hostname_of_API_URL() or wiki_API.site_name()
*
* @type {RegExp}
*
* @see PATTERN_PROJECT_CODE
* @see PATTERN_URL_GLOBAL, PATTERN_URL_WITH_PROTOCOL_GLOBAL,
* PATTERN_URL_prefix, PATTERN_WIKI_URL, PATTERN_wiki_project_URL,
* PATTERN_external_link_global
*/
var PATTERN_wiki_project_URL = /^(https?:)?(?:\/\/)?(([a-z][a-z\d\-]{0,14})\.([a-z]+)+(?:\.[a-z]+)+)/i;
// @see wiki_API.api_URL()
function hostname_of_API_URL(API_URL) {
if (!/\/api\.php$/.test(API_URL))
return;
var url = new library_namespace.URI(API_URL);
return url && url.hostname;
}
/**
* Get the API URL of specified project.
*
* project = language_code.family
*
* @param {String}project
* wiki project, domain or language. 指定維基百科語言/姊妹計劃<br />
* e.g., 'en', 'en.wikisource'.
*
* @returns {String}API URL
*
* @see https://en.wikipedia.org/wiki/Wikipedia:Wikimedia_sister_projects
* TODO:
* https://zh.wikipedia.org/wiki/Wikipedia:%E5%A7%8A%E5%A6%B9%E8%AE%A1%E5%88%92#.E9.93.BE.E6.8E.A5.E5.9E.8B
*/
function api_URL(project, options) {
if (!project) {
var session = wiki_API.session_of_options(options);
return session && session.API_URL || wiki_API.API_URL;
}
project = String(project);
var lower_case = project.toLowerCase();
if (lower_case in api_URL.alias) {
project = api_URL.alias[lower_case];
}
library_namespace.debug('project: ' + project, 3, 'api_URL');
// PATTERN_PROJECT_CODE_i.test(undefined) === true
if (PATTERN_PROJECT_CODE_i.test(project)) {
if (lower_case in api_URL.wikimedia) {
project += '.wikimedia';
} else if (lower_case in api_URL.family) {
// (wiki_API.language || 'www') + '.' + project
project = wiki_API.language + '.' + project;
} else if (/wik[it]/i.test(project)) {
// e.g., 'mediawiki' → 'www.mediawiki'
// e.g., 'wikidata' → 'www.wikidata'
project = 'www.' + project;
} else {
// e.g., 'en' → 'en.wikipedia' ({{SERVERNAME}})
// e.g., 'zh-yue' → 'zh-yue.wikipedia', 'zh-classical'
// e.g., 'test2' → 'test2.wikipedia' ({{SERVERNAME}})
project += '.wikipedia';
}
}
// @see PATTERN_PROJECT_CODE
if (/^[a-z][a-z\d\-]{0,14}\.[a-z]+$/i.test(project)) {
// e.g., 'en.wikisource', 'en.wiktionary'
project += '.org';
}
// console.trace([ wiki_API.API_URL, project ]);
var url = new library_namespace.URI(project,
//
/:\/\//.test(wiki_API.API_URL) && wiki_API.API_URL);
if (url && url.hostname) {
// 先測試是否為自訂 API。
return /\/api\.php$/.test(project) ? project
// e.g., '//zh.wikipedia.org/'
// e.g., 'https://www.mediawiki.org/w/api.php'
// e.g., 'https://www.mediawiki.org/wiki/'
: (url.protocol || api_URL.default_protocol || 'https:') + '//'
+ url.hostname + '/w/api.php';
}
library_namespace.error('api_URL: Unknown project: [' + project
+ ']! Using default API URL.');
return wiki_API.API_URL;
}
// the key MUST in lower case!
// @see https://www.wikimedia.org/
// @see [[Special:Interwiki]] 跨維基資料 跨 wiki 字首
api_URL.wikimedia = {
meta : true,
commons : true,
species : true,
incubator : true,
// mul : true,
phabricator : true,
wikitech : true,
// https://quarry.wmflabs.org/
quarry : true,
releases : true
}
// shortcut, namespace aliases.
// the key MUST in lower case!
// @see [[m:Help:Interwiki linking#Project titles and shortcuts]],
// [[mw:Manual:InitialiseSettings.php]]
// https://noc.wikimedia.org/conf/highlight.php?file=InitialiseSettings.php
// [[:zh:Help:跨语言链接#出現在正文中的連結]]
// @see [[Special:Interwiki]] 跨維基資料 跨 wiki 字首
// @see two-letter project code shortcuts
// [[m:Requests_for_comment/Set_short_project_namespace_aliases_by_default_globally]]
api_URL.alias = {
// project with language prefix
// project: language.*.org
w : 'wikipedia',
n : 'wikinews',
// 維基教科書
b : 'wikibooks',
q : 'wikiquote',
s : 'wikisource',
// 維基學院
v : 'wikiversity',
voy : 'wikivoyage',
wikt : 'wiktionary',
// project: *.wikimedia.org
m : 'meta',
// 這一項會自動判別語言。
metawikipedia : 'meta',
c : 'commons',
wikispecies : 'species',
phab : 'phabricator',
download : 'releases',
// project: www.*.org
d : 'wikidata',
mw : 'mediawiki',
wmf : 'wikimedia',
betawikiversity : 'beta.wikiversity'
};
// families must with language prefix
// the key MUST in lower case!
api_URL.family = 'wikipedia|wikibooks|wikinews|wikiquote|wikisource|wikiversity|wikivoyage|wiktionary'
.split('|').to_hash();
// api_URL.shortcut_of_project[project] = alias
api_URL.shortcut_of_project = Object.create(null);
Object.keys(api_URL.alias).forEach(function(shortcut) {
api_URL.shortcut_of_project[api_URL.alias[shortcut]] = shortcut;
});
/**
* setup API URL.
*
* @param {wiki_API}session
* 正作業中之 wiki_API instance。
* @param {String}[API_URL]
* language code or API URL of MediaWiki project
*
* @inner
*/
function setup_API_URL(session, API_URL) {
library_namespace.debug('API_URL: ' + API_URL + ', default language: '
+ wiki_API.language, 3, 'setup_API_URL');
// console.log(session);
// console.trace(wiki_API.language);
if (API_URL === true) {
// force to login.
API_URL = session.API_URL || wiki_API.API_URL;
}
if (API_URL && typeof API_URL === 'string'
// && wiki_API.is_wiki_API(session)
) {
session.API_URL = api_URL(API_URL);
// remove cache
delete session.last_page;
delete session[KEY_HOST_SESSION];
// force to login again: see wiki_API.login
// 據測試,不同 projects 間之 token 不能通用。
delete session.token.csrftoken;
delete session.token.lgtoken;
// library_namespace.set_debug(6);
if (library_namespace.platform.nodejs) {
// 初始化 agent。
// create and keep a new agent. 維持一個獨立的 agent。
// 以不同 agent 應對不同 host。
var agent = library_namespace.application.net
//
.Ajax.setup_node_net(session.API_URL);
session.get_URL_options = {
// start_time : Date.now(),
// API_URL : session.API_URL,
agent : agent,
headers : Object.create(null)
};
if (false) {
// set User-Agent to use:
// Special:ApiFeatureUsage&wpagent=CeJS script_name
wiki.get_URL_options.headers['User-Agent'] = library_namespace.get_URL.default_user_agent;
}
} else {
// e.g., 老舊版本 or using XMLHttpRequest @ WWW
session.get_URL_options = {};
}
}
// TODO: 這只是簡陋的判別方法。
var matched = wiki_API.site_name(session, {
get_all_properties : true
});
// console.trace(matched);
if (matched && (matched.family in api_URL.family)) {
// e.g., "wikipedia"
session.family = matched.family;
}
}
// @see set_default_language(), language_to_site_name()
function setup_API_language(session, language_code) {
if (!language_code || typeof language_code !== 'string')
return;
language_code = language_code.toLowerCase();
var site_name = wiki_API.site_name(language_code,
add_session_to_options(session, {
get_all_properties : true
}));
if (site_name && site_name.language
&& site_name.language !== 'multilingual') {
// e.g., API_URL=zh.wiktionary
language_code = site_name.language;
}
if (PATTERN_PROJECT_CODE_i.test(language_code)
// 不包括 test2.wikipedia.org 之類。
&& !/^test|wik[it]/i.test(language_code)
// 排除 'Talk', 'User', 'Help', 'File', ...
&& !(session.configurations
// ↑ session === wiki_API?
&& session.configurations.namespace_pattern || get_namespace.pattern)
.test(language_code)) {
if (language_code === 'simple') {
session.first_damain_name = language_code;
// [[w:en:Basic English]]
// language_code = 'en-basiceng';
language_code = 'en';
} else if (language_code in wiki_API.language_code_to_site_alias) {
// e.g., 'cmn'
language_code = wiki_API.language_code_to_site_alias[language_code];
}
// [[m:List of Wikipedias]]
session.language
// e.g., 'zh-classical', 'zh-yue', 'zh-min-nan'
= language_code;
site_name = wiki_API.site_name(session, {
get_all_properties : true
});
// console.trace([ language_code, site_name ]);
if (site_name.language === 'multilingual'
// e.g., language_code === 'commons'
&& language_code === site_name.project) {
// default: English
session.language = 'en';
}
site_name = site_name.site;
var time_interval_config = wiki_API.query.edit_time_interval;
// apply local lag interval rule.
if (!(session.edit_time_interval >= 0)
&& ((site_name in time_interval_config) || (language_code in time_interval_config))) {
session.edit_time_interval = time_interval_config[site_name]
|| time_interval_config[language_code];
library_namespace.debug('Use interval '
+ session.edit_time_interval + ' for language '
+ language_code, 1, 'setup_API_language');
}
}
}
// --------------------------------------------------------------------------------------------
// @inner
function get_first_domain_name_of_session(session) {
var first_damain_name;
if (session) {
first_damain_name =
// e.g., 'simple'
session.first_damain_name
// assert: typeof session.API_URL === 'string'
// 注意:在取得 page 後,中途更改過 API_URL 的話,session.language 會取得錯誤的資訊!
|| session.language
// 應該採用來自宿主 host session 的 language. @see setup_data_session()
|| get_first_domain_name_of_session(session[KEY_HOST_SESSION]);
}
return first_damain_name;
}
// [[en:Help:Interwikimedia_links]] [[Special:Interwiki]]
// https://zh.wikipedia.org/wiki/Special:GoToInterwiki/testwiki:
// TODO: link prefix: e.g., 'zh:n:' for zh.wikinews
// [[:phab:T102533]]
// [[:gerrit:gitweb?p=mediawiki/core.git;a=blob;f=RELEASE-NOTES-1.23]]
/**
* language code → Wikidata site code / Wikidata site name / Wikimedia
* project name. get_project()<br />
* 將語言代碼轉為 Wikidata API 可使用之 site name。 [[yue:]] → zh-yue → zh_yuewiki。 亦可自
* options 取得 wikidata API 所須之 site parameter。
*
* @example<code>
// e.g., 'enwiki', 'zhwiki', 'enwikinews'
CeL.wiki.site_name(wiki)
</code>
*
* @param {String|wiki_API}language
* 語言代碼。 language / family / project code / session. default
* language: wiki_API.language e.g., 'en', 'zh-classical', 'ja',
* ...
* @param {Object}[options]
* 附加參數/設定選擇性/特殊功能與選項<br />
* options.get_all_properties: return
* {language,family,site,API_URL}
*
* @returns {String}wiki_project, Wikidata API 可使用之 site name parameter。
*
* @see mediaWiki.config.get('wgWikiID')
* https://www.mediawiki.org/wiki/ResourceLoader/Core_modules#mediaWiki.config
* @see set_default_language()
* @see [[:en:Help:Interwiki linking#Project titles and shortcuts]],
* [[:zh:Help:跨语言链接#出現在正文中的連結]]
* @see [[meta:List of Wikimedia projects by size]]
* @see [[m:List of Wikipedias]] IETF language tag language code for
* gettext()
* @see https://www.wikidata.org/w/api.php?action=help&modules=wbgetentities
*
* @since 2017/9/4 20:57:8 整合原先的 language_to_project(),
* language_to_site_name()
*/
function language_to_site_name(language, options) {
// console.trace(language);
var session;
// 擷取出維基姊妹項目各種 type: 先要能擷取出 language code + family
// types: 'API', 'db', 'site', 'link', 'dump', ...
// 不能保證 wiki_API.is_wiki_API(language) → is_Object(language),
// 因此使用 typeof。
if (language && (typeof language === 'object'
// || language === wiki_API
)) {
// treat language as options with session.
session = wiki_API.session_of_options(language);
// options.language 較 session 的設定優先。
// language.language
language = get_first_domain_name_of_session(language)
// wikidata 沒有 session.language,會用
// session[KEY_HOST_SESSION].language。
|| get_first_domain_name_of_session(session)
// || language
;
if (false && typeof language === 'object')
console.trace(language);
} else if (typeof language === 'function') {
throw new Error('Invalid type of language: ' + typeof language);
}
// console.log(session);
// console.trace(session.family);
/**
* Wikimedia project / family. e.g., wikipedia, wikinews, wiktionary.
* assert: family && /^wik[it][a-z]{0,9}$/.test(family)
*
* @type {String}
*/
var family;
/** {wiki_API}in what wiki session */
var in_session;
if (typeof options === 'string') {
// shift arguments
family = options;
options = null;
} else {
in_session = wiki_API.session_of_options(options);
family = options && options.family;
}
// console.trace(language);
var page_name;
var matched = typeof language === 'string' && !language.includes('://')
&& language.match(/^[:\s]*(\w+):(?:(\w+):)?(.*)/);
if (matched) {
matched.family = api_URL.alias[matched[1]];
page_name = matched[3];
if (matched.family) {
if (matched[2]) {
// e.g., "n:zh:title"
language = matched[2];
} else {
// e.g., "n:", "n:zh", "n:title"
language = matched[3];
}
} else if (matched.family = api_URL.alias[matched[2]]) {
// e.g., "zh:n:title"
language = matched[1];
} else {
// e.g., "zh:title"
language = matched[1];
}
family = family || matched.family;
}
// console.trace(language);
matched = wiki_API.namespace(language, options);
// console.trace([ matched, language ]);
if (matched && !isNaN(matched)
//
&& (matched !== wiki_API.namespace.hash.project
// e.g., 'wikidata'
|| language.trim().toLowerCase() === 'project')) {
// e.g., input "language" of [[Category:title]]
// 光是只有 "Category",代表還是在本 wiki 中,不算外語言。
language = null;
}
// console.trace(language);
// console.trace(in_session);
// 正規化。
language = String(language
// || in_session && in_session.language
|| get_first_domain_name_of_session(in_session)
// else use default language
// 警告: 若是沒有輸入,則會直接回傳預設的語言。因此您或許需要先檢測是不是設定了 language。
|| wiki_API.language).trim().toLowerCase();
// zh_yue → zh-yue
language = language.replace(/[_ ]/g, '-');
// console.trace(language);
var API_URL;
var interwiki_pattern = in_session && in_session.configurations
&& in_session.configurations.interwiki_pattern;
var interwikimap = library_namespace.is_RegExp(interwiki_pattern)
&& in_session.latest_site_configurations
&& in_session.latest_site_configurations.interwikimap;
// console.trace([ interwiki_pattern, interwikimap ]);
if (Array.isArray(interwikimap)) {
matched = language.match(interwiki_pattern);
if (matched && interwikimap.some(function(map) {
if (map.prefix === matched[1]) {
// console.log(map);
// API_URL = map.url;
return matched = map
//
.url.replace(/\/wiki\/\$1/, '/w/api.php')
//
.replace(/\$1/, '');
}
})) {
language = matched;
}
} else if (language in language_code_to_site_alias) {
// e.g., 'lzh' → 'zh-classical'
language = language_code_to_site_alias[language];
} else if (!family && session && !session.family
&& !session[KEY_HOST_SESSION] && session.API_URL) {
// e.g., API_URL: 'https://zh.moegirl.org.cn/api.php'
// console.trace([ language, family ]);
language = session.API_URL;
}
var site, project,
// 是為猜測的語言。
is_guessing_language;
matched = language
// e.g., 'zh-min-nan' → 'zh_min_nan'
.replace(/-/g, '_')
// 'zhwikinews' → zh.wikinews
.match(PATTERN_SITE);
if (matched) {
language = matched[1];
family = family || matched[2];
} else if (matched = language.match(/^[a-z\d\-]{2,13}$/)) {
// e.g., 'zh-classical', 'zh-min-nan'
language = matched[0];
if (language === 'wikidata') {
family = language;
language = 'www';
}
// console.trace([ language, family ]);
} else if (matched = wiki_API.hostname_of_API_URL(language)) {
// treat language as API_URL.
API_URL = language;
// console.trace(matched);
// console.trace(session);
library_namespace.debug(language, 4, 'language_to_site_name');
if (library_namespace.is_IP(matched)) {
// We cannot get information from IP.
matched = [ matched.replace(/\./g, '_') ];
} else {
matched = matched.split('.');
if (matched.length === 2) {
// e.g., "lingualibre.org"
matched.unshift('');
}
}
/**
* 去掉 '.org' 之類。 language-code.wikipedia.org e.g.,
* zh-classical.wikipedia.org
*/
family = family || matched[1];
// incase 'https://test.wikidata.org/w/api.php'
language = !/^test|wik[it]/i.test(matched[0]) && matched[0];
if (!language) {
is_guessing_language = true;
language = wiki_API.language;
}
} else if (matched = language.match(/^([a-z\d\-_]+)\.([a-z\d\-_]+)/)) {
language = matched[1];
family = family || matched[2];
} else {
library_namespace.error('language_to_site_name: Invalid language: '
+ language);
if (false) {
console.trace([ language,
wiki_API.hostname_of_API_URL(language), session,
in_session ]);
}
}
// console.trace(family);
family = family || session && session.family || in_session
&& in_session.family;
// console.trace(family);
if (!family || family === 'wiki')
family = 'wikipedia';
if (false) {
console.trace([ API_URL, session && session.API_URL, language,
family ]);
}
API_URL = API_URL || session && session.API_URL
|| api_URL(language + '.' + family);
// console.trace(API_URL);
if (family === 'wikidata') {
// wikidatawiki_p
site = family + 'wiki';
} else if (family === 'wikimedia' && language === 'en'
//
|| (site = API_URL.match(/\/\/([\w]+)\./))
// e.g., API_URL === 'https://test.wikipedia.org/w/api.php'
&& /^test/i.test(site = site[1])) {
// e.g., @ console @ https://commons.wikimedia.org/
project = site;
// assert: (project in wiki_API.api_URL.wikimedia)
// 'commonswiki'
site += 'wiki';
} else {
site = is_guessing_language ? '' : language.toLowerCase().replace(
/-/g, '_');
// e.g., 'zh' + 'wikinews' → 'zhwikinews'
site += (family === 'wikipedia'
// using "commonswiki" instead of "commonswikimedia"
|| (language in wiki_API.api_URL.wikimedia) ? 'wiki' : family);
}
// console.trace(site);
library_namespace.debug(site, 3, 'language_to_site_name');
project = project || language === 'www' ? family
: language in wiki_API.api_URL.wikimedia ? language : null;
if (project) {
// e.g., get from API_URL
// wikidata, commons: multilingual
language = 'multilingual';
} else {
project = is_guessing_language ? family : language + '.' + family;
}
if (is_guessing_language && session && session.language) {
language = session.language;
is_guessing_language = false;
}
// throw site;
if (options && options.get_all_properties) {
var family_prefix = wiki_API.api_URL.shortcut_of_project[family];
// for API_URL==="https://lingualibre.org/api.php",
// is_guessing_language=true && family_prefix===undefined
site = {
// en, zh
language : language,
is_guessing_language : is_guessing_language,
// family: 'wikipedia' (default), 'wikimedia',
// wikibooks|wiktionary|wikiquote|wikisource|wikinews|wikiversity|wikivoyage
family : family,
family_prefix : family_prefix,
// interwikimap prefix. 在像是 https://lingualibre.org/ 的情況下不設定
interwiki_prefix : (is_guessing_language ? undefined
: (family_prefix || family) + ':' + language + ':'),
// Wikimedia project name: wikidata, commons, zh.wikipedia
project : project,
// wikidata API 所須之 site name parameter。 wikiID
// site_namewiki for Wikidata API. e.g., zh-classical →
// zh_classicalwiki
// for database: e.g., zh-classical → zh_classicalwiki_p
// e.g., 'zhwiki'. `.wikiid` @ siteinfo
// Also for dump: e.g., 'zhwikinews'
// https://dumps.wikimedia.org/backup-index.html
// @see wikidatawiki_p.wb_items_per_site.ips_site_id
// wikidatawiki, commonswiki, zhwiki
site : site,
// API URL (default): e.g.,
// https://en.wikipedia.org/w/api.php
// https://www.wikidata.org/w/api.php
API_URL : API_URL
};
if (session
// session === wiki_API?
&& session.configurations
// e.g., for Fandom sites
&& session.configurations.sitename) {
site.sitename = session.configurations.sitename;
}
if (session && session.site_name) {
site.site_name = session.site_name;
}
var project = session && session.latest_site_configurations
&& session.latest_site_configurations.general.wikiid;
if (project) {
site.wikiid = project;
}
if (page_name) {
site.page_name = page_name;
}
}
// assert: {String}site
return session && session.site_name || site;
}
// --------------------------------------------------------------------------------------------
/**
* get NO of namespace
*
* 注意: [[d:Q1]], [[en:T]] 之 namespace 亦為 0。須採 session.is_article() 測試。
*
* @param {String|Integer}namespace
* namespace or page title
* @param {Object}[options]
* 附加參數/設定選擇性/特殊功能與選項
*
* @returns {Integer|String|Undefined}namespace NO.
*/
function get_namespace(namespace, options) {
options = library_namespace.setup_options(options);
var is_page_title = options.is_page_title;
if (wiki_API.is_page_data(namespace)) {
namespace = namespace.title;
is_page_title = true;
}
if (!is_page_title && (namespace == Math.floor(namespace))) {
if (options.get_name) {
// e.g., `wiki.namespace(NS_Category, {get_name: true});`
namespace = String(namespace);
} else {
// {Integer}namespace
return namespace;
}
}
var session = session_of_options(options);
var namespace_hash = options.namespace_hash || session
// session === wiki_API?
&& session.configurations && session.configurations.namespace_hash
|| get_namespace.hash;
if (Array.isArray(namespace)) {
namespace = namespace.join('|');
}
// console.log(namespace);
if (typeof namespace === 'string') {
var list = [];
// e.g., 'main{{!}}template' → 'main|template'
namespace = prefix_page_name(namespace)
// e.g., 'User_talk' → 'User talk'
.replace(/[\s_]+/g, ' ');
(is_page_title ? [ namespace.toLowerCase() ]
//
: namespace.toLowerCase()
// for ',Template,Category', ';Template;Category',
// 'main|file|module|template|category|help|portal|プロジェクト'
// https://www.mediawiki.org/w/api.php?action=help&modules=main#main.2Fdatatypes
.split(/(?:[,;|\u001F]|%7C|%1F)/)).forEach(function(n) {
if (is_page_title && n.startsWith(':')) {
// e.g., [[:title]]
n = n.slice(1);
}
if (false && n.startsWith(':')) {
// Invalid page title / namespace
list.push(undefined);
}
// get namespace `_n` only.
// e.g., 'wikipedia:sandbox' → 'wikipedia'
var _n = n.includes(':') ? n.replace(/:.*$/, '').trim()
// e.g., get_namespace('Wikipedia', {...})
: is_page_title ? 0 : n;
if (!_n) {
// _n === ''
list.push(0);
return;
}
if (!is_page_title && (!isNaN(_n)
// 要指定所有值,請使用*。 To specify all values, use *.
|| _n === '*')) {
// {Integer}_n
list.push(_n);
return;
}
if (_n in namespace_hash) {
list.push(namespace_hash[_n]);
return;
}
if (is_page_title) {
list.push(0);
return;
}
if (namespace_hash === get_namespace.hash) {
// console.trace(namespace);
library_namespace.debug('Invalid namespace: ['
//
+ n + '] @ namespace list ' + namespace,
//
2, 'get_namespace');
// console.trace(arguments);
} else {
list.push(is_page_title === false
// main article space
// is_page_title === false 亦即
// options.is_namespace === true
&& _n !== 'main' ? undefined : 0);
}
});
if (list.length === 0) {
return;
}
// list.sort().unique_sorted().join('|');
list = list.unique();
if (options.get_name) {
var name_of_NO = options.name_of_NO || session
// session === wiki_API?
&& session.configurations && session.configurations.name_of_NO
|| get_namespace.name_of_NO;
list = list.map(function(namespace_NO) {
return namespace_NO in name_of_NO
//
? name_of_NO[namespace_NO] : namespace_NO;
});
}
return list.length === 1 ? list[0] : list.join('|');
}
if (namespace) {
library_namespace.warn('get_namespace: Invalid namespace: ['
+ namespace + ']');
// console.trace(arguments);
}
return;
}
/**
* The namespace number of the page. 列舉型別 (enumeration)
*
* assert: 正規名稱必須擺在最後一個,供 function namespace_text_of_NO() 使用。
*
* CeL.wiki.namespace.hash
*
* {{NAMESPACENUMBER:{{FULLPAGENAME}}}}
*
* @type {Object}
*
* @see https://en.wikipedia.org/wiki/Wikipedia:Namespace
*/
get_namespace.hash = {
// Virtual namespaces
media : -2,
special : -1,
// 0: (Main/Article) main namespace 主要(條目內容/內文)命名空間/識別領域
// 條目 条目 entry 文章 article: ns = 0, 頁面 page: ns = any. 章節/段落 section
main : 0,
'' : 0,
// 討論對話頁面
talk : 1,
// 使用者頁面
user : 2,
'user talk' : 3,
// ----------------------------
// NS_PROJECT
// the project namespace for matters about the project
// Varies between wikis
project : 4,
// https://meta.wikimedia.org/wiki/Requests_for_comment/Set_short_project_namespace_aliases_by_default_globally
// [[w:ja:Wikipedia:バグの報告#WPショートカットが機能しない]]
// [[phab:rOMWCa30603ab09d162fd30ff4081f85054df81a0ae49]]
// https://noc.wikimedia.org/conf/highlight.php?file=InitialiseSettings.php
wp : 4,
wb : 4,
wv : 4,
ws : 4,
wn : 4,
wq : 4,
wt : 4,
// WD : 4,
wikidata : 4,
// [[commons:title]] @ enwiki 會造成混亂
// commons : 4,
// COM : 4,
// https://en.wikinews.org/wiki/Help:Namespace
// WN : 4,
wikinews : 4,
// WP : 4,
// 正規名稱必須擺在最後一個,供 function namespace_text_of_NO() 使用。
wikipedia : 4,
// ----------------------------
// Varies between wikis
'project talk' : 5,
// 正規名稱必須擺在最後一個,供 function namespace_text_of_NO() 使用。
'wikipedia talk' : 5,
// image
file : 6,
'file talk' : 7,
// [[MediaWiki:title]]
mediawiki : 8,
'mediawiki talk' : 9,
模板 : 10,
テンプレート : 10,
plantilla : 10,
틀 : 10,
template : 10,
'template talk' : 11,
// [[Help:title]], [[使用說明:title]]
// H : 12,
// 正規名稱必須擺在最後一個,供 function namespace_text_of_NO() 使用。
help : 12,
'help talk' : 13,
// https://commons.wikimedia.org/wiki/Commons:Administrators%27_noticeboard#Cleaning_up_after_creation_of_CAT:_namespace_redirect
// CAT : 14,
// 正規名稱必須擺在最後一個,供 function namespace_text_of_NO() 使用。
category : 14,
'category talk' : 15,
// 主題/主題首頁
portal : 100,
// 主題討論
'portal talk' : 101,
book : 108,
'book talk' : 109,
draft : 118,
'draft talk' : 119,
// Education Program
'education program' : 446,
// Education Program talk
'education program talk' : 447,
// TimedText
timedtext : 710,
// TimedText talk
'timedtext talk' : 711,
// 模块 模塊 模組
module : 828,
'module talk' : 829,
// Gadget
gadget : 2300,
'gadget talk' : 2301,
// Gadget definition
'gadget definition' : 2302,
'gadget definition talk' : 2303,
// 話題 The Flow namespace (prefix Topic:)
topic : 2600
};
// Should use `CeL.wiki.namespace.name_of(NS, session)`
// NOT `wiki_API.namespace.name_of_NO[NS]`
get_namespace.name_of_NO = [];
/**
* build session.configurations.namespace_pattern || get_namespace.pattern
*
* @inner
*/
function generate_namespace_pattern(namespace_hash, name_of_NO) {
var source = [];
for ( var namespace in namespace_hash) {
name_of_NO[namespace_hash[namespace]] = upper_case_initial(
namespace)
// [[Mediawiki talk:]] → [[MediaWiki talk:]]
.replace(/^Mediawiki/, 'MediaWiki');
if (namespace)
source.push(namespace);
}
// namespace_pattern matched: [ , namespace, title ]
return new RegExp('^(' + source.join('|').replace(/ /g, '[ _]')
+ '):(.+)$', 'i');
}
get_namespace.pattern = generate_namespace_pattern(get_namespace.hash,
get_namespace.name_of_NO);
// console.log(get_namespace.pattern);
function namespace_text_of_NO(NS, options) {
if (!NS)
return '';
var session = session_of_options(options);
if (NS === session
// session === wiki_API?
&& session.configurations && session.configurations.namespace_hash ? session.configurations.namespace_hash.wikipedia
: get_namespace.hash.wikipedia) {
if (session && session.family) {
return wiki_API.upper_case_initial(
// e.g., commons, wikidata
get_wikimedia_project_name(session) || session.family);
}
// e.g., testwiki:
return 'Wikipedia';
}
var name_of_NO = session
// session === wiki_API?
&& session.configurations && session.configurations.name_of_NO
|| wiki_API.namespace.name_of_NO;
return wiki_API.upper_case_initial(name_of_NO[NS]);
}
// CeL.wiki.namespace.name_of(NS, session)
get_namespace.name_of = namespace_text_of_NO;
/**
* remove namespace part of the title. 剝離 namespace。
*
* wiki.remove_namespace(), wiki_API.remove_namespace()
*
* TODO: wiki.remove_namespace(page, only_remove_this_namespace)
*
* @param {String}page_title
* page title 頁面標題。
* @param {Object}[options]
* 附加參數/設定選擇性/特殊功能與選項
*
* @returns {String}title without namespace
*/
function remove_page_title_namespace(page_title, options) {
page_title = wiki_API.normalize_title(page_title, options);
// console.trace(page_title);
if (Array.isArray(page_title)) {
return page_title.map(function(_page_title) {
return remove_page_title_namespace(_page_title, options);
});
}
if (typeof page_title !== 'string') {
library_namespace.debug(page_title, 5,
'remove_page_title_namespace');
return page_title;
}
var session = session_of_options(options);
var namespace_pattern = session
// session === wiki_API?
&& session.configurations && session.configurations.namespace_pattern
|| get_namespace.pattern;
if (page_title.endsWith(':')) {
// e.g., input "Template:"
page_title += ' ';
}
var matched = page_title.match(namespace_pattern);
library_namespace.debug('Test ' + wiki_API.title_link_of(page_title)
+ ', get [' + matched + '] using pattern ' + namespace_pattern,
4, 'remove_page_title_namespace');
if (matched) {
// namespace_pattern matched: [ , namespace, title ]
return (matched ? matched[2] : page_title).trim();
// do not normalize page title.
}
// Leave untouched
return page_title;
}
function namespace_of_options(options) {
var namespace = Array.isArray(options) ? options
// 必須預防 {Object}options。
: !options ? 0 : typeof options === 'number' ? options
: typeof options === 'string' ? library_namespace
.is_digits(options) ? +options : options
: options.namespace;
return namespace;
}
// TODO: is_namespace(page_title, 'Wikipedia|User')
function page_title_is_namespace(page_title, options) {
var namespace = namespace_of_options(options);
var page_ns;
// console.trace(namespace, wiki_API.is_page_data(page_title));
if (wiki_API.is_page_data(page_title)) {
page_ns = page_title.ns;
} else {
page_title = wiki_API.normalize_title(page_title, options);
page_ns = get_namespace(page_title, Object.assign({
// for wiki_API.namespace()
is_page_title : true
}, options));
}
function check_namespace(namespace) {
// 預防 jawiki.namespace('Draft') === undefined 這情況下被當作 true。
var namespace_to_test = get_namespace(namespace, Object.assign({
is_page_title : false
}, options));
if (namespace_to_test === undefined)
namespace_to_test = namespace;
return page_ns === namespace_to_test;
}
if (Array.isArray(namespace)) {
// e.g., `CeL.wiki.is_namespace('User:user', ['Wikipedia', 'User'])`
return namespace.some(check_namespace);
}
return check_namespace(namespace);
}
function convert_page_title_to_namespace(page_title, options) {
var namespace = namespace_of_options(options);
namespace = get_namespace(namespace, Object.assign({
get_name : true
}, options)) + ':';
page_title = wiki_API.normalize_title(page_title, options);
// console.trace(page_title);
function to_namespace(page_title) {
return page_title || page_title === 0 ? namespace
+ remove_page_title_namespace(page_title, options)
: page_title;
}
if (Array.isArray(page_title)) {
return page_title.map(to_namespace);
}
return to_namespace(page_title);
}
// ------------------------------------------
function is_talk_namespace(namespace, options) {
options = Object.assign({
// for wiki_API.namespace()
is_page_title : true
}, options);
// wiki_API.is_page_data(namespace, options) ||
// wiki_API.is_Page(namespace)
if (typeof namespace === 'object') {
namespace = namespace.ns >= 0 ? namespace.ns : namespace.title;
}
if (typeof namespace === 'string') {
namespace = wiki_API.normalize_title(namespace, options)
.toLowerCase();
var session = session_of_options(options);
var name_of_NO = session
// session === wiki_API?
&& session.configurations && session.configurations.name_of_NO
|| wiki_API.namespace.name_of_NO;
if (session) {
// assert: {Number|Undefined}namespace
namespace = wiki_API.namespace(namespace, options);
} else {
// treat ((namespace)) as page title
// get namespace only. e.g., 'wikipedia:sandbox' → 'wikipedia'
if (namespace.includes(':')) {
// get namespace only, remove page title
var _namespace = namespace.replace(/:.*$/, '');
if (_namespace in name_of_NO)
namespace = name_of_NO[_namespace];
else
return PATTERN_talk_prefix.test(namespace)
|| PATTERN_talk_namespace_prefix
.test(namespace);
}
namespace = +namespace;
}
}
if (typeof namespace === 'number') {
// 單數: talk page
return namespace > 0 && namespace % 2 === 1;
}
}
// 討論頁面不應包含 [[Special talk:*]]。
function to_talk_page(page_title, options) {
options = Object.assign({
// for wiki_API.namespace()
is_page_title : true
}, options);
// console.trace([ page_title, options ]);
var session = session_of_options(options), namespace;
if (wiki_API.is_page_data(page_title)) {
// assert: {Number}namespace
namespace = page_title.ns;
page_title = wiki_API.title_of(page_title);
} else {
page_title = wiki_API.normalize_title(page_title, options);
// console.trace([ page_title ]);
if (!session) {