/**
* @name Wikiapi.js
*
* @fileoverview Main codes of module wikiapi (class Wikiapi)
*/
'use strict';
/**
* @description CeJS controller
*
* @type Function
* @inner
*
* @see https://github.com/kanasimi/CeJS
*/
let CeL;
try {
// Load CeJS library.
CeL = require('cejs');
if (typeof CeL.then === 'function' && typeof window === "object" && window.CeL) {
// assert: @Snowpack
CeL = window.CeL;
}
} catch (e) /* istanbul ignore next: Only for debugging locally */ {
// https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md
// const Wikiapi = require('./wikiapi.js');
require('./_CeL.loader.nodejs.js');
CeL = globalThis.CeL;
}
// assert: typeof CeL === 'function'
// Load modules.
// @see `wiki loader.js`:
// https://github.com/kanasimi/wikibot/blob/master/wiki%20loader.js
CeL.run(['interact.DOM', 'application.debug',
// 載入不同地區語言的功能 for wiki.work()。
'application.locale',
// 載入操作維基百科的主要功能。
'application.net.wiki',
// Optional 可選功能
'application.net.wiki.data', 'application.net.wiki.admin',
// Add color to console messages. 添加主控端報告的顏色。
'interact.console',
// for 'application.platform.nodejs': CeL.env.arg_hash, wiki_API.cache(),
// CeL.fs_mkdir(), wiki_API.read_dump()
'application.storage']);
// --------------------------------------------------------
/**
* @description syntactic sugar for CeJS MediaWiki module. CeL.net.wiki === CeL.wiki
*
* @inner
*/
const wiki_API = CeL.net.wiki;
/**
* key to get {@link wiki_API} operator when using {@link wiki_API}.
*
* @type Symbol
*
* @inner
*/
const KEY_SESSION = wiki_API.KEY_SESSION;
// Set default language. 改變預設之語言。
wiki_API.set_language('en');
/**
* @description key to get {@link wiki_API} operator inside {@link Wikiapi}.
* this[KEY_wiki_session]
inside module code will get {@link wiki_API} operator.
*
* @type Symbol
*
* @inner
*/
const KEY_wiki_session = Symbol('wiki_API session');
// for debug
// Wikiapi.KEY_wiki_session = KEY_wiki_session;
/**
* @description main Wikiapi operator 操作子.
*
* @param {String|Object} [API_URL] - language code or service endpoint of MediaWiki project.
* Input {Object} will be treat as options.
*
* @class
*/
function Wikiapi(API_URL) {
const wiki_session = new wiki_API(null, null, API_URL);
// this[KEY_wiki_session] = new wiki_API(null, null, API_URL);
setup_wiki_session.call(this, wiki_session);
}
// --------------------------------------------------------
/**
* @description Bind {@link wiki_API} instance to {@link Wikiapi} instance
*
* @param {wiki_API} wiki_session - wiki_API session
*
* @inner
*/
function setup_wiki_session(wiki_session) {
Object.defineProperty(this, KEY_wiki_session, {
value: wiki_session,
writable: true,
});
}
/**
* @alias login
* @description login into the target MediaWiki API using the provided username and password.
* For bots, see [[Special:BotPasswords]] on your wiki.
*
* @param {String} user_name - Account username.
* @param {String} password - Account's password.
* @param {String} [API_URL] - API URL of target wiki site.
*
* @returns {Promise} Promise object represents {String} login_name
*
* @example
const wiki = new Wikiapi;
const login_options = {
user_name: '', password: '', API_URL: 'en',
// Ror lingualibre only. @see https://github.com/kanasimi/wikibot/blob/master/wiki%20configuration.sample.js
//data_API_URL: 'https://lingualibre.org/api.php',
//SPARQL_API_URL: 'https://lingualibre.org/bigdata/namespace/wdq/sparql',
// Calling in another domain
origin: '*'
};
await wiki.login(login_options);
//
*
* @example
const wiki = new Wikiapi;
await wiki.login('user_name', 'password', 'en');
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_login(user_name, password, API_URL) {
let options;
if (!password && !API_URL && CeL.is_Object(user_name)) {
options = user_name;
} else if (CeL.is_Object(API_URL)) {
options = { ...API_URL, user_name, password };
} else {
options = { user_name, password, API_URL };
}
function Wikiapi_login_executor(resolve, reject) {
const wiki_session = wiki_API.login({
preserve_password: true,
...options,
API_URL: options.API_URL || this[KEY_wiki_session].API_URL,
callback(login_name, error) {
if (error) {
reject(error);
} else {
resolve(login_name);
}
},
// task_configuration_page: 'page title',
});
setup_wiki_session.call(this, wiki_session);
}
return new Promise(Wikiapi_login_executor.bind(this));
}
// --------------------------------------------------------
/**
* @description attributes of {Object} page_data, will setup by {@link set_page_data_attributes}.
*
* @type Object
*
* @inner
*/
const page_data_attributes = {
/**
* @description get {String}page content, maybe undefined.
* 條目/頁面內容 = wiki_API.revision_content(revision)
*
* @type String
*/
wikitext: {
get() {
// console.trace(this);
// console.log(wiki_API.content_of(this, 0));
return wiki_API.content_of(this, 0);
}
},
/**
* @description get {Object}revisions
*
* @type Object
*/
revision: {
value: function revision(revision_NO) {
return wiki_API.content_of(this, revision_NO);
}
},
/**
* @description get {Attay} parsed data of page_data
*
* @type Array
*/
parse: {
value: function parse(options) {
// this === page_data
// options = { ...options, [KEY_SESSION]: this[KEY_wiki_session] };
options = Wikiapi.prototype.append_session_to_options.call(this, options);
// using function parse_page(options) @ wiki_API
return wiki_API.parser(this, options).parse();
// return {Array}parsed
}
},
};
/**
* @description Bind {@link page_data_attributes} to page_data
*
* @param {Object} page_data - page data
* @param {wiki_API} wiki - wiki_API session
*
* @returns {Promise} Promise object represents {Object} page's data
*
* @inner
*/
function set_page_data_attributes(page_data, wiki) {
// `page_data` maybe non-object when error occurres.
if (page_data) {
page_data[KEY_wiki_session] = wiki;
Object.defineProperties(page_data, page_data_attributes);
}
return page_data;
}
/**
* @alias page
* @description given a title, returns the page's data.
*
* @param {String} title - page title
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {Object} page's data
*
* @example
// on Wikipedia...
const wiki = new Wikiapi('en');
// ...or other MediaWiki websites
//const wiki = new Wikiapi('https://awoiaf.westeros.org/api.php');
let page_data = await wiki.page('Universe', {
// You may also set rvprop.
//rvprop: 'ids|content|timestamp|user',
});
console.log(page_data.wikitext);
//
*
* @example
const wiki = new Wikiapi;
let page_data = await wiki.page('Universe', {
// Get multi revisions
revisions: 2
});
console.log(page_data.wikitext);
//
*
* @example
// Usage with other language
const zhwiki = new Wikiapi('zh');
await zhwiki.login('user', 'password');
let page_data = await zhwiki.page('Universe');
// `page_data.parse(options)` will startup the parser process, create page_data.parsed. After .parse(), we can use parsed.each().
const parsed = page_data.parse();
// See all type in wiki_toString @ https://github.com/kanasimi/CeJS/tree/master/application/net/wiki/parser/wikitext.js
// List all template name.
parsed.each('template', token => console.log(token.name));
// List all [[Template:Tl]] token.
parsed.each('Template:Tl', token => console.log(token));
//
*
* @example
const wiki = new Wikiapi('en');
const page_data = await wiki.page('JavaScript');
const parsed = page_data.parse();
let infobox;
// Read Infobox templates, convert to JSON.
parsed.each('template', template_token => {
if (template_token.name.startsWith('Infobox')) {
infobox = template_token.parameters;
return parsed.each.exit;
}
});
for (const [key, value] of Object.entries(infobox))
infobox[key] = value.toString();
// print json of the infobox
console.log(infobox);
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_page(title, options) {
function Wikiapi_page_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
wiki.page(title, (page_data, error) => {
if (error) {
reject(error);
} else {
resolve(set_page_data_attributes(page_data, wiki));
}
}, {
// node.js v12.22.7: Cannot use "?."
rvlimit: options && options.revisions,
...options
});
}
return new Promise(Wikiapi_page_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias tracking_revisions
* @description tracking revisions to lookup what revision had added / removed to_search
.
*
* @param {String} title - page title
* @param {String} to_search - filter / text to search. to_search(diff, revision, old_revision): `diff` 為從舊的版本 `old_revision` 改成 `revision` 時的差異。
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {Object} newer_revision,
* newer_revision.page: page_data
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_tracking_revisions(title, to_search, options) {
function Wikiapi_tracking_revisions_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
wiki.tracking_revisions(title, to_search, (revision, page_data, error) => {
if (error) {
reject(error);
} else {
if (!revision)
revision = Object.create(null);
revision.page = page_data;
resolve(revision);
}
}, options);
}
return new Promise(Wikiapi_tracking_revisions_executor.bind(this));
}
// --------------------------------------------------------
/**
* @description Handle the result of MediaWiki API when executing edit operation.
*
* @param {Function} reject - reject function
* @param {any} error - error object / message
* @param {any} [result] - result of MediaWiki API
*
* @returns {Boolean} Return true
if the edit operation failed.
*
* @inner
*/
function reject_edit_error(reject, error, result) {
// skip_edit is not error
if (!error
// @see wiki_API_edit.check_data
|| Array.isArray(error) && error[0] === Wikiapi.skip_edit[0]) {
return;
}
if (Array.isArray(error) && typeof error[1] === 'string') {
// console.log('' + reject);
// console.trace(error);
error = error[1];
const error_object = new Error(error);
error_object.from_string = error;
error = error_object
// console.log(error);
}
if (result && typeof error === 'object')
error.result = result;
reject(error);
return true;
}
/**
* @alias edit_page
* @description edits content of target page.
const enwiki = new Wikiapi;
await enwiki.login('bot name', 'password', 'en');
const SB_page_data = await enwiki.page('Wikipedia:Sandbox');
// You may do some operations on SB_page_data
const parsed = SB_page_data.parse();
parsed.each('template', template_token => {
// modify template token
});
// and then edit it. ** You MUST call enwiki.page() before enwiki.edit()! **
await enwiki.edit(parsed.toString(), { bot: 1, minor: 1, nocreate: 1 });
// exmaple 2: append text in the tail of page content
await enwiki.edit(page_data => {
return page_data.wikitext
+ '\nTest edit using {{GitHub|kanasimi/wikiapi}}.';
}, { bot: 1 });
// exmaple 3: replace page content
await enwiki.edit('Just replace by this wikitext', { bot: 1, minor: 1, nocreate: 1, summary: 'test edit' });
// exmaple 4: append a new section
await enwiki.edit('section content', {
section: 'new',
sectiontitle: 'section title',
nocreate : 1,
summary: 'test edit',
});
//
*
* @example
const enwiki = new Wikiapi;
await enwiki.login('bot name', 'password', 'en');
await enwiki.edit_page('Wikipedia:Sandbox', function (page_data) {
this.summary += ': You may set additional summary inside the function';
delete this.minor;
return page_data.wikitext
+ '\nTest edit using {{GitHub|kanasimi/wikiapi}}.';
}, { bot: 1, nocreate: 1, minor: 1, redirects: 1, summary: 'test edit' });
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_edit_page(title, content, options) {
function Wikiapi_edit_page_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
// console.trace([title, content]);
// console.trace(`Wikiapi_edit_page 1: ${wiki_API.title_link_of(title)}, ${wiki.actions.length} actions, ${wiki.running}/${wiki.thread_count}/${wiki.actions[wiki_API.KEY_waiting_callback_result_relying_on_this]}.`);
// console.trace(title);
// CeL.set_debug(6);
if (title) {
// console.trace(wiki);
options = { ...options, error_with_symbol: true };
// 預防 page 本身是非法的頁面標題。當 session.page() 出錯時,將導致沒有 .last_page。
if (wiki_API.is_page_data(title))
options.task_page_data = title;
// call wiki_API_prototype_method() @ CeL.application.net.wiki.list
wiki.page(title, (page_data, error) => {
// console.trace('Set .page_to_edit:');
// console.log([title, page_data, error]);
// console.log(wiki.actions[0]);
// 手動指定要編輯的頁面。避免多執行續打亂 wiki.last_page。
options.page_to_edit = page_data;
}, options);
}
// console.trace(`Wikiapi_edit_page 2: ${wiki_API.title_link_of(title)}, ${wiki.actions.length} actions, ${wiki.running}/${wiki.thread_count}/${wiki.actions[wiki_API.KEY_waiting_callback_result_relying_on_this]}.`);
// console.trace(wiki);
// console.trace(wiki.last_page);
// wiki.edit(page contents, options, callback)
wiki.edit(typeof content === 'function' ? function (page_data) {
return content.call(this, set_page_data_attributes(page_data, wiki));
} : content, options, (title, error, result) => {
// console.trace('Wikiapi_edit_page: callbacked');
// console.log(title);
// console.log(wiki.running);
// CeL.set_debug(6);
if (!reject_edit_error(reject, error, result)) {
// console.log('Wikiapi_edit_page: resolve');
resolve(title);
}
// console.log('Wikiapi_edit_page: callback() return');
});
// console.trace(`Wikiapi_edit_page 3: ${wiki_API.title_link_of(title)}, ${wiki.actions.length} actions, ${wiki.running}/${wiki.thread_count}/${wiki.actions[wiki_API.KEY_waiting_callback_result_relying_on_this]}.`);
}
return new Promise(Wikiapi_edit_page_executor.bind(this));
}
// return Wikiapi.skip_edit;
as a symbol to skip this edit, do not generate
// warning message.
// 可以利用 ((return [ wiki_API.edit.cancel, 'reason' ];)) 來回傳 reason。
// ((return [ wiki_API.edit.cancel, 'skip' ];)) 來跳過 (skip) 本次編輯動作,不特別顯示或處理。
// 被 skip/pass 的話,連警告都不顯現,當作正常狀況。
/**
* @description Return Wikiapi.skip_edit
when we running edit function, but do not want to edit current page.
*
* @memberof Wikiapi
*/
Wikiapi.skip_edit = [wiki_API.edit.cancel, 'skip'];
// --------------------------------------------------------
/**
* @alias move_page
* @description Move page move_from_title
to move_to_title
.
*
* @param {Object|String} move_from_title - move from title
* @param {Object|String} move_to_title - move to title
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {String} result of MediaWiki API
*
* @example move_from_title
to move_to_title
.
await wiki.move_page(move_from_title, move_to_title, { reason, noredirect: true, movetalk: true });
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_move_page(move_from_title, move_to_title, options) {
function Wikiapi_move_page_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
// using wiki_API.prototype.move_page()
wiki.move_page(move_from_title, move_to_title, options, (data, error) => {
if (error) {
/**
*
e.g., { code: 'articleexists', info: 'A page of that name already exists, or the name you have chosen is not valid. Please choose another name.', '*': '...' }
e.g., { code: 'missingtitle', info: "The page you specified doesn't exist.", '*': '...' }
*/
reject(error);
} else {
/**
*
e.g., { from: 'from', to: 'to', reason: 'move', redirectcreated: '', moveoverredirect: '' }
*/
resolve(data);
}
}, options);
}
return new Promise(Wikiapi_move_page_executor.bind(this));
}
/**
* @alias move_to
* @description Move to move_to_title
. Must call {@link Wikiapi#page} first!
*
* @param {Object|String} move_to_title - move to title
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {String} result of MediaWiki API
*
* @example move_from_title
to move_to_title
.
page_data = await wiki.page(move_from_title);
await wiki.move_to(move_to_title, { reason: reason, noredirect: true, movetalk: true });
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_move_to(move_to_title, options) {
function Wikiapi_move_to_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
if (!wiki.last_page) {
reject(new Error(Wikiapi_move_to.name + ': Must call .page() first! '
// gettext_config:{"id":"cannot-move-to-$1"}
+ CeL.gettext('Cannot move to %1', wiki_API.title_link_of(move_to_title))));
return;
}
// using wiki_API.prototype.move_to()
wiki.move_to(move_to_title, options, (data, error) => {
if (error) {
/**
*
e.g., { code: 'articleexists', info: 'A page of that name already exists, or the name you have chosen is not valid. Please choose another name.', '*': '...' }
e.g., { code: 'missingtitle', info: "The page you specified doesn't exist.", '*': '...' }
*/
reject(error);
} else {
/**
*
e.g., { from: 'from', to: 'to', reason: 'move', redirectcreated: '', moveoverredirect: '' }
*/
resolve(data);
}
}, options);
}
return new Promise(Wikiapi_move_to_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias query
* @description query MediaWiki API manually
*
* @param {Object} parameters - parameters to call MediaWiki API
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {Object} result of MediaWiki API
*
* @example
const wiki = new Wikiapi('mediawiki');
const results = await wiki.query({
action: "flow-parsoid-utils",
content: "bold & italic",
title: "MediaWiki", from: "html", to: "wikitext"
});
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_query(parameters, options) {
function Wikiapi_query_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
wiki.query_API(parameters, (data, error) => {
if (error) {
reject(error);
} else {
resolve(data);
}
}, {
post_data_only: true,
...options
});
}
return new Promise(Wikiapi_query_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias purge
* @description Purge the cache for the given title.
*
* @param {Object} title - page title
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {Object} page_data
*
* @example
const metawiki = new Wikiapi('meta');
let page_data = await metawiki.purge('Project:Sandbox');
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_purge(title, options) {
if (CeL.is_Object(title) && !options) {
// shift arguments.
[title, options] = [null, title];
}
function Wikiapi_purge_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
if (title) {
wiki.page(title);
}
// using wiki_API.purge
wiki.purge((data, error) => {
if (error) {
reject(error);
} else {
resolve(data);
}
}, options);
}
return new Promise(Wikiapi_purge_executor.bind(this));
}
// --------------------------------------------------------
/**
* @description Bind properties to {@link wiki_API} data entity.
* 設定 wikidata entity object,讓我們能直接操作 entity.modify(),並且避免洩露 wiki_API session。
*
* @param {Object} data_entity - wiki_API data entity
*
* @inner
*/
function setup_data_entity(data_entity) {
if (!data_entity)
return;
// assert: data_entity[KEY_SESSION].host === this
// console.trace(data_entity[KEY_SESSION].host === this);
delete data_entity[KEY_SESSION];
Object.defineProperties(data_entity, {
[KEY_wiki_session]: { value: this },
modify: { value: modify_data_entity },
});
}
/**
* @description Modify data entity
*
* @param {Object} data_entity - wiki_API data entity
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {Object} result data entity
*
* @inner
*/
function modify_data_entity(data_to_modify, options) {
function modify_data_entity_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
// console.trace(wiki);
// using function wikidata_edit() @
// https://github.com/kanasimi/CeJS/blob/master/application/net/wiki/data.js
// wiki.edit_data(id, data, options, callback)
wiki.data(this).edit_data(data_to_modify || this, options, (data_entity, error) => {
// console.trace([data_entity, error]);
if (error) {
reject(error);
} else {
setup_data_entity.call(wiki, data_entity);
resolve(data_entity);
}
});
}
return new Promise(modify_data_entity_executor.bind(this));
}
/**
* @alias data
* @description Get wikidata entity / property
*
* @param {Object} data_entity - wiki_API data entity
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {Object} wikidata entity / property
*
* @example
const wiki = new Wikiapi;
const data_entity = await wiki.data('Q1');
// Work with other language
console.assert(CeL.wiki.data.value_of(data_entity.labels.zh) === '宇宙');
//
*
* @example
const wiki = new Wikiapi;
const page_data = await wiki.page('Human');
const data_entity = await wiki.data(page_data);
console.assert(CeL.wiki.data.value_of(data_entity.labels.zh) === '人類');
//
*
* @example
const wiki = new Wikiapi;
// Read, access by title (English), access property P1419
let data = await wiki.data('Universe', 'P1419');
// assert: {Array}data = [ 'shape of the universe', '', ... ]
console.assert(data.includes('shape of the universe'));
//
*
* @example
// Just for test
delete CeL.wiki.query.default_maxlag;
const wiki = new Wikiapi;
await wiki.login('user', 'password', 'test');
// Get https://test.wikidata.org/wiki/Q7
let entity = await wiki.data('Q7');
// search [ language, label ]
//entity = await wiki.data(['en', 'Earth']);
// Reset claim
entity = await wiki.data('Q1841');
await entity.modify({ claims: [{ P3: "old.wav", remove: true }] }, { bot: 1, summary: 'test edit: Remove specific value' });
// Warning: If you want to perform multiple operations on the same property, you need to get the entity again!
entity = await wiki.data('Q1841');
await entity.modify({ claims: [{ P3: "new.wav" }] }, { bot: 1, summary: 'test edit: Add value' });
// Update claim
await entity.modify({ claims: [{ P17: 'Q213280' }] }, { bot: 1, summary: 'test edit: Update claim' });
// Update claim: set country (P17) to 'Test Country 1' (Q213280) ([language, label] as entity)
await entity.modify({ claims: [{ language: 'en', country: [, 'Test Country 1'] }] }, { summary: '' });
// Remove country (P17) : 'Test Country 1' (Q213280)
await entity.modify({ claims: [{ language: 'en', country: [, 'Test Country 1'], remove: true }] }, { summary: '' });
// Update label
await entity.modify({ labels: [{ language: 'zh-tw', value: '地球' }] }, { summary: '' });
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_data(key, property, options) {
if (CeL.is_Object(property) && !options) {
// shift arguments.
[property, options] = [null, property];
}
function Wikiapi_data_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
if (false && wiki_API.is_page_data(key)) {
// get entity (wikidata item) of page_data: key
// .page(key): 僅僅設定 .last_page,不會真的再獲取一次頁面內容。
wiki.page(key);
}
if (key.title && !key.site) {
// @see function wikidata_entity() @ CeL.application.net.wiki.data
// 確保引用到的是本 wiki session,不會引用到其他 site。
key = { ...key, site: this.site_name() };
}
// using wikidata_entity() → wikidata_datavalue()
wiki.data(key, property, (data_entity, error) => {
if (error) {
reject(error);
} else {
setup_data_entity.call(wiki, data_entity);
resolve(data_entity);
}
}, options);
}
return new Promise(Wikiapi_data_executor.bind(this));
}
/**
* @alias new_data_entity
* @description Create new entity or property
*
* @param {Object} data_to_modify - Initial data.
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {Object} new entity or property.
*
* @example
const new_entity = await wiki.new_data_entity({ labels: { en: "Evolution in Mendelian Populations" }, P698: "17246615", P932: "1201091" }, { new: 'item' });
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_new_data_entity(data_to_modify, options) {
function Wikiapi_new_data_entity_executor(resolve, reject) {
options = { new: 'item', ...options };
const wiki = this[KEY_wiki_session];
wiki.edit_data({}, options, (data_entity, error) => {
if (error) {
reject(error);
} else if (data_to_modify) {
delete options.new;
//console.trace([data_entity, options]);
wiki.edit_data(data_entity, data_to_modify, options, (result, error) => {
if (error) {
reject(error);
} else if (false && options.retrieve_entity) {
// reget modified data
this.data(data_entity.id, options).then(resolve, reject);
} else {
//console.trace([data_entity, result]);
//data_entity.latest_result = result;
// data_entity: e.g.,
// {"type":"item","id":"Q123456","labels":{},"descriptions":{},"aliases":{},"claims":{},"sitelinks":{},"lastrevid":123456}
resolve(data_entity);
}
});
} else {
setup_data_entity.call(wiki, data_entity);
resolve(data_entity);
}
});
}
return new Promise(Wikiapi_new_data_entity_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias SPARQL
* @description Query wikidata via SPARQL
*
* @param {Object} SPARQL - SPARQL to query. Please test it on Wikidata Query Service first.
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {Array} query result of `SPARQL`.
*
* @example
const wikidata_item_list = await wiki.SPARQL(`
SELECT ?item ?itemLabel WHERE {
?item wdt:P31 wd:Q146.
SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
}
`);
//
*
* @example
// for case-insensitive DOI
const wikidata_item_list = await wiki.search('haswbstatement:' + JSON.stringify('P356=10.1371/journal.pone.0029797'), { namespace: 0 });
//wikidata_item_list.map(item => item.title)
// for case-sensitive DOI
const wikidata_item_list = await wiki.SPARQL(`
SELECT ?doi ?item ?itemLabel WHERE {
VALUES ?doi { "10.1371/JOURNAL.PONE.0029797" }
?item wdt:P356 ?doi.
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}`, {
// options.API_URL: custom SPARQL endpoint
API_URL: ''
});
//wikidata_item_list.id_list()
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_SPARQL(SPARQL, options) {
function Wikiapi_SPARQL_executor(resolve, reject) {
wiki_API.SPARQL(SPARQL, (result, error) => {
if (error) {
reject(error);
} else {
resolve(result);
}
}, this.append_session_to_options(options));
}
return new Promise(Wikiapi_SPARQL_executor.bind(this));
}
// --------------------------------------------------------
/**
*
* @example
const wiki = new Wikiapi;
let list = await wiki.categorymembers('Chemical elements');
console.log(list);
// Working on multiple pages
await wiki.for_each_page(
// {Array} title liat / page data list
list,
page_data => {
// ...
});
//
*
* @example
const wiki = new Wikiapi;
let list = await wiki.embeddedin('Template:Periodic table');
console.log(list);
//
*/
// Warning: Won't throw if title is not existed!
// @inner
function Wikiapi_list(list_type, title, options) {
function Wikiapi_list_executor(resolve, reject) {
options = CeL.setup_options(options);
// const wiki = this[KEY_wiki_session];
wiki_API.list(title, (list/* , target, options */) => {
// console.trace(list);
if (list.error) {
reject(list.error);
} else {
resolve(list);
}
}, this.append_session_to_options({
type: list_type,
// namespace: '0|1',
...options
}));
/**
*
// method 2: 使用循環取得資料版:
wiki.cache({
// Do not write cache file to disk.
cache: false,
type: list_type,
list: title
}, (list, error) => {
if (error) {
reject(error);
} else {
resolve(list);
}
},
// default options === this
//{ namespace : '0|1' }
options);
// NG: 不應使用單次版
wiki[list_type](title, (list, error) => {
if (error) {
reject(error);
} else {
resolve(list);
}
}, {
limit: 'max', ...options
});
*/
}
return new Promise(Wikiapi_list_executor.bind(this));
}
// functions for several kinds of lists
function Wikiapi_for_each(type, title, for_each, options) {
return Wikiapi_list.call(this, type, title, {
for_each,
...options
});
}
// --------------------------------------------------------
/**
* @alias category_tree
* @description Get structural category tree with sub-categories of root_category
. This is powerful than categorymembers. Get sub-categories with {@link Wikiapi.KEY_subcategories}.
*
* @param {String} root_category - category name
* @param {Object} [options] - options to run this function.
*
* @returns {Promise} Promise object represents {Array} category_tree.
*
* @example
const enwiki = new Wikiapi('en');
const page_list = await enwiki.category_tree('Countries in North America', 1);
assert(page_list.some(page_data => page_data.title === 'United States'), 'list category tree: [[Category:Countries in North America]] must includes [[United States]]');
assert('Mexico' in page_list[Wikiapi.KEY_subcategories], 'list category tree: [[Category:Mexico]] is a subcategory of [[Category:Countries in North America]]');
//
*
* @example
const wiki = new Wikiapi('commons');
const all_sub_categories = (await wiki.category_tree('Echinodermata', { depth: 2, cmtype: 'subcat', get_flated_subcategories: true })).flated_subcategories;
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_category_tree(root_category, options) {
function Wikiapi_category_tree_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
// using wiki_API.prototype.category_tree
wiki.category_tree(root_category, (list, error) => {
if (error) {
reject(error);
} else {
resolve(list);
}
}, options);
}
return new Promise(Wikiapi_category_tree_executor.bind(this));
}
/**
* export key for subcategory 子分類 used in {@link Wikiapi#category_tree}
*
* @example
//
const KEY_subcategories = Wikiapi.KEY_subcategories;
//
*/
Wikiapi.KEY_subcategories = wiki_API.KEY_subcategories;
// --------------------------------------------------------
/**
* @alias search
* @description search pages include key
*
* @param {String} key - key to search
* @param {Object} [options] - options to run this function.
*
* @returns {Promise} Promise object represents {Array} page_list.
*
* @example
const zhwikinews = new Wikiapi('zh.wikinews');
const page_list = await zhwikinews.search('"霍金"');
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_search(key, options) {
function Wikiapi_search_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
// using wiki_API.search
wiki.search(key, (list, error) => {
if (error) {
reject(error);
} else {
resolve(list);
}
}, options);
}
return new Promise(Wikiapi_search_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias redirects_root
* @description Get redirects target of title
.
*
* @param {String} title - page title
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {String} page title or {Object} page data
*
* @example
const redirects_taregt = await enwiki.redirects_root('WP:SB', { get_page_data: true });
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_redirects_root(title, options) {
function Wikiapi_redirects_root_executor(resolve, reject) {
// const wiki = this[KEY_wiki_session];
// using wiki_API.redirects_root
wiki_API.redirects_root(title, (_title, page_data, error) => {
if (error) {
reject(error);
} else if (options && options.get_page_data) {
page_data.query_title = title;
resolve(page_data);
} else {
resolve(_title);
}
}, this.append_session_to_options(options));
}
return new Promise(Wikiapi_redirects_root_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias redirects_here
* @description Get all pages redirects to title
.
*
* @param {String} title - page title
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents {Array} redirect_list
*
* @example
const redirects_list = await enwiki.redirects_here('Wikipedia:Sandbox');
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_redirects_here(title, options) {
function Wikiapi_redirects_here_executor(resolve, reject) {
// const wiki = this[KEY_wiki_session];
// using wiki_API.redirects_here
wiki_API.redirects_here(title, (root_page_data, redirect_list, error) => {
if (error) {
reject(error);
} else {
//console.trace(root_page_data);
//console.trace(redirect_list);
//console.assert(!redirect_list || redirect_list === root_page_data.redirect_list);
resolve(redirect_list || root_page_data);
}
}, this.append_session_to_options({
// Making .redirect_list[0] the redirect target.
include_root: true,
...options
}));
}
return new Promise(Wikiapi_redirects_here_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias register_redirects
* @description register page alias. usually used for templates
*
* @param {Array|String} page_title_list - list of page titles
* @param {Object} [options] - options to run this function.
*
* @returns {Promise} Promise object represents the operations are done.
*
* @example
const wiki_session = new Wikiapi;
// e.g., await wiki_session.register_redirects(['Section link', 'Broken anchors'], { namespace: 'Template' });
await wiki_session.register_redirects([template_name_1, template_name_2, template_name_3], { namespace: 'Template' });
// ...
const page_data = await wiki_session.page(page_title);
// {Array} parsed page content 頁面解析後的結構。
const parsed = page_data.parse();
parsed.each('Template:' + template_name_1, function (token, index, parent) {
// ...
});
parsed.each('template', function (token, index, parent) {
if (wiki_session.is_template(template_name_1, token)) {
// ...
return;
}
if (wiki_session.is_template(template_name_2, token)) {
// ...
return;
}
// alternative method:
switch (wiki_session.redirect_target_of(token)) {
case wiki_session.redirect_target_of(template_name_1):
break;
case wiki_session.redirect_target_of(template_name_2):
break;
case wiki_session.redirect_target_of(template_name_3):
break;
}
});
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_register_redirects(page_title_list, options) {
function Wikiapi_register_redirects_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
wiki.register_redirects(page_title_list, (redirect_list, error) => {
if (error) {
reject(error);
} else {
// console.trace( redirect_list);
resolve(redirect_list);
}
}, options);
}
return new Promise(Wikiapi_register_redirects_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias upload
* @description Upload specified local file to the target wiki.
*
* @param {Object} file_data - Upload configurations.comment
will only show in the file page. The text
, ... till categories
will all ignored. If you want to update the content of file page, please consider Variable_Map
as mentioned in the sample code.file_path
: string - Local path.media_url
: string - URL path. Alternative to file_path
.comment
: string - Upload comment.text
: string or {Object} - Either {String}wikitext to fill the file's page,description
: string - File description.date
: date string - YYYY-MM-DD, e.g., new Date()
|| '2021-01-01'
.source_url
: string - Source where the file comes from, typically an URL.author
: string - Author's name or username in wikicode, e.g., URL or '[[User:Yoda|Yoda]]'
.permission
: string - License and other usage limitations and warnings, e.g., '{{cc-by-sa-2.5}}'
.other_versions
: string - Wikicode links to files with very similar content or derived files.other_fields
: string - Additional table fields added on the bottom of the template.license
: array of strings - License under which the file is uploaded, e.g., ['{{cc-by-sa-2.5}}']
.additional_text
: string - Additional wikitext to place before categories
.categories
: array of strings - Categories for this file, e.g., ['[[Category:test images]]']
.ignorewarnings
: boolean - Set to 1 will overwrite existing files.file_datafor other
file_data
options.
*
* @returns {Promise} Promise object represents {String} result of MediaWiki API
*
* @example
const wiki = new Wikiapi;
await wiki.login('user', 'password', 'test');
// Upload a local file directly:
//let result = await wiki.upload({ file_path: '/local/file/path', comment: '', text: '' || {description: '', ...} });
let result = await wiki.upload({
file_path: '/local/file/path', comment: '',
filename: 'Will set via .file_path or .media_url if not settled.',
description: '', date: new Date() || '2021-01-01', source_url: 'https://github.com/kanasimi/wikiapi', author: '[[User:user]]', permission: '{{cc-by-sa-2.5}}', other_versions: '', other_fields: '',
license: ['{{cc-by-sa-2.5}}'], categories: ['[[Category:test images]]'],
bot: 1, tags: "tag1|tag2",
// To overwrite existing file
ignorewarnings: 1,
});
// Upload file from URL:
result = await wiki.upload({ media_url: 'https://media.url/name.jpg', comment: '', text: '' });
//
*
* @example
const wiki = new Wikiapi;
await wiki.login('user', 'password', 'test');
const variable_Map = new CeL.wiki.Variable_Map();
variable_Map.set('description', '...');
//variable_Map.set('date', '...');
// ...
//variable_Map.set('other_fields', '...');
let result = await wiki.upload({
file_path: '/local/file/path',
// The comment
will only show in the file page when updating file. It is read-only and cannot be modified.
comment: '',
// CeL.wiki.Variable_Map
is used to update content when update pages or files. It will insert comments around the value, prevent others from accidentally editing the text that will be overwritten.
// description
till other_fields
will be auto-setted as values assigned above.
// The code to do the conversion is in wiki_API.upload
@ https://github.com/kanasimi/CeJS/blob/master/application/net/wiki/edit.js
// There are some examples: https://github.com/kanasimi/wikibot/blob/master/routine/20181016.import_earthquake_shakemap.js https://github.com/kanasimi/wikibot/blob/master/routine/20190629.import_hurricane_track_maps.js
// More examples to use CeL.wiki.Variable_Map
: https://github.com/kanasimi/wikibot/blob/master/routine/20191129.check_language_convention.js
variable_Map,
// When set .variable_Map, after successful update, the content of file page will be auto-updated too.
// To overwrite existing file
ignorewarnings: 1,
});
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_upload(file_data) {
// 2021/3/25 renamed from old name: Wikiapi_upload_file(),
// Wikiapi_upload_file_executor()
function Wikiapi_upload_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
wiki.upload(file_data, (result, error) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
}
return new Promise(Wikiapi_upload_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias download
* @description Download file to local path.
*
* @param {String} file_title - file title starts with "File:"
* @param {Object} [options] - options to run this function. Refer to example codes.
*
* @returns {Promise} Promise object represents [ {Object}file informations ]
*
* @example
const wiki = new Wikiapi('commons');
await wiki.download('File:Example.svg');
//
*
* @example
const wiki = new Wikiapi('commons');
// Download non-vector version of .svg
await wiki.download('File:Example.svg', { width: 80 });
// Change width / height
await wiki.download('File:Example.png', {
file_name: 'example.png', directory: '/tmp/',
// reget and overwrite existed file.
reget: true,
width: 80,// height: 80
});
// Download all files from a (Commons) category and its subcategories WITH directory structure.
const file_data_list = await wiki.download('Category:name', {
directory: './',
max_threads: 4,
// depth of categories
depth: 4,
// Only download files with these formats.
//download_derivatives : ['wav', 'mp3', 'ogg'],
// Warning: Will skip downloading if there is no new file!
download_derivatives : 'mp3',
// A function to filter result pages. Return `true` if you want to keep the element.
page_filter(page_data) {
return page_data.title.includes('word');
}
});
// Download all files from a (Commons) category WITHOUT directory structure.
for (const page_data of await wiki.categorymembers('Category:name', { namespace: 'File' })) {
try {
//if (wiki.is_namespace(page_data, 'File'))
const file_data = await wiki.download(page_data, { directory: './' });
} catch (e) { console.error(e); }
}
// also
const categorymembers = await wiki.categorymembers('Category:name', { namespace: 'File' });
const file_data_list = await wiki.download(categorymembers, { directory: './', no_category_tree: true });
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_download(file_title, options) {
function Wikiapi_download_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
wiki.download(file_title, options, (result, error) => {
if (error) {
// return result.error_titles
reject(result && result.error_titles && result || error);
} else {
resolve(result);
}
});
}
return new Promise(Wikiapi_download_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias for_each_page
* @description Edit / process pages listing in page_list
. Will get the content of multiple pages at once to save transmission times. 一次取得多個頁面內容,以節省傳輸次數。
*
* @param {Array} page_list - title list or page_data list
* @param {Function} for_each_page - processor for each page. for_each_page(page_data with contents)
* @param {Object} [options] - options to run this function. Refer to example codes.
*
* @returns {Promise} Promise object represents the operations are done.
*
* @example
const enwiki = new Wikiapi('en');
const link_from = await wiki.redirects_here('ABC');
await wiki.for_each_page(link_from, page_data => {
// Return `Wikiapi.skip_edit` if you just want to get the page data.
return Wikiapi.skip_edit;
return 'You may also modify page contents for each page';
}, {
// The options below are sample, not default configuration.
// denotes we do not edit pages
no_edit: true,
// Only needed if you want to modify page.
summary: 'test edit',
// Allow content to be emptied. 允許內容被清空。白紙化。
allow_empty: true,
tags: 'bot trial',
// prevent creating new pages
// Throw an error if the page doesn't exist.
// 若頁面不存在/已刪除,則產生錯誤。
nocreate: 1,
// denotes this is a bot edit. 標記此編輯為機器人編輯。
bot: 1,
minor: 1,
// options to get page revisions
page_options: { redirects: 1, rvprop: 'ids|content|timestamp|user' }
// .for_each_page()
will generate a report. It can be written to the specified page.
log_to: 'log to this page',
// no warning messages on console. e.g., hide "wiki_API_page: No contents: [[title]]" messages
no_warning: true,
// no warning messages and debug messages on console
no_message: true,
});
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_for_each_page(page_list, for_each_page, options) {
function Wikiapi_for_each_page_executor(resolve, reject) {
options = typeof options === 'string' ? { summary: options } : CeL.setup_options(options);
const wiki = this[KEY_wiki_session];
const append_to_this = Array.isArray(for_each_page) && for_each_page[1];
if (Array.isArray(for_each_page))
for_each_page = for_each_page[0];
// console.trace(for_each_page);
const work_config = {
log_to: null,
no_message: options.no_edit,
...options,
//is_async_each: CeL.is_async_function(for_each_page),
each: [function each(page_data/* , messages, config */) {
set_page_data_attributes(page_data, wiki);
return for_each_page.apply(this, arguments);
}, append_to_this],
// Run after all list items (pages) processed.
last(error) {
// this === options
// console.trace('last(error)');
// console.error(error);
// console.trace('Wikiapi_for_each_page_executor finish:');
// console.log(options);
// 提早執行 resolve(), reject() 的話,可能導致後續的程式碼 `options.last`
// 延後執行,程式碼順序錯亂。
if (typeof options.last === 'function')
options.last.call(this, error);
if (error) {
if (options.throw_error) {
reject(error);
return;
}
console.error(error);
}
resolve(this);
}
};
wiki.work(work_config, page_list);
}
return new Promise(Wikiapi_for_each_page_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias convert_Chinese
* @description convert text to traditional Chinese / simplified Chinese.
*
* @param {String|Array|Object} text - text or objects to convert. Will convert to {String} using JSON.stringify().
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents the converted text.
*
* @example
const wiki = new Wikiapi('en');
await wiki.convert_Chinese('中国', { uselang: 'zh-hant' });
await wiki.convert_Chinese('中國', { uselang: 'zh-hans' });
await wiki.convert_Chinese(['繁體', '簡體'], { uselang: 'zh-hans' });
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_convert_Chinese(text, options) {
function Wikiapi_convert_Chinese(resolve, reject) {
if (typeof options === 'string') {
options = { uselang: options };
}
const site_name = this.site_name({ get_all_properties: true });
// node.js v12.22.7: Cannot use "?."
if (site_name && site_name.language === 'zh') {
// 不用再重新造出一個實體。
options = this.append_session_to_options(options);
}
// using wiki_API.search
wiki_API.convert_Chinese(text, (text, error) => {
if (error) {
reject(error);
} else {
resolve(text);
}
}, options);
}
return new Promise(Wikiapi_convert_Chinese.bind(this));
}
// --------------------------------------------------------
// May only test in the [https://tools.wmflabs.org/ Wikimedia Toolforge]
function Wikiapi_run_SQL(SQL, for_each_row/* , options */) {
function Wikiapi_run_SQL_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
function run_callback() {
wiki.SQL_session.SQL(SQL, (error, rows/* , fields */) => {
if (error) {
reject(error);
} else {
rows.forEach(for_each_row);
}
});
resolve();
}
if (wiki.SQL_session) {
run_callback();
return;
}
wiki.SQL_session = new wiki_API.SQL((error, rows, fields) => {
if (error) {
reject(error);
} else {
run_callback();
}
}, wiki);
}
return new Promise(Wikiapi_run_SQL_executor.bind(this));
}
// --------------------------------------------------------
function Wikiapi_setup_layout_elements(options) {
function Wikiapi_setup_layout_elements_executor(resolve, reject) {
// const wiki = this[KEY_wiki_session];
wiki_API.setup_layout_elements(resolve, this.append_session_to_options(options));
}
return new Promise(Wikiapi_setup_layout_elements_executor.bind(this));
}
// --------------------------------------------------------
/**
* @alias get_featured_content
* @description Get featured content.
*
* @param {String|Object} [options] - options to run this function.
* {String}type (FFA|GA|FA|FL)
* || {type,on_conflict(FC_title, {from,to})}
*
* @returns {Promise} Promise object represents {Object} featured content data hash
*
* @example
// MUST including wiki.featured_content first to get featured content!
CeL.run('application.net.wiki.featured_content');
// ...
const FC_data_hash = await wiki.get_featured_content();
console.assert(FC_data_hash === wiki.FC_data_hash);
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_get_featured_content(options) {
if (!options || !options.type) {
const session = this;
return Wikiapi_get_featured_content.default_types
.reduce((promise, type) => promise.then(Wikiapi_get_featured_content.bind(session, { ...options, type })), Promise.resolve());
if (false) {
let promise = Promise.resolve();
Wikiapi_get_featured_content.default_types.forEach(type => {
promise = promise.then(Wikiapi_get_featured_content.bind(session, { ...options, type }));
});
return promise;
}
}
function Wikiapi_get_featured_content_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
wiki.get_featured_content(options, FC_data_hash => {
try {
this.FC_data_hash = FC_data_hash;
resolve(FC_data_hash);
} catch (e) {
reject(e);
}
});
}
return new Promise(Wikiapi_get_featured_content_executor.bind(this));
}
Wikiapi_get_featured_content.default_types = 'FFA|GA|FA|FL'.split('|');
// --------------------------------------------------------
/**
* @alias site_name
* @description Get site name / project name of this {Wikiapi}.
*
* @param {String} [language] - language code of wiki session
* @param {Object} [options] - options to run this function
*
* @returns {String}site name
*
* @example
console.log(Wikiapi.site_name('zh', { get_all_properties: true }));
const wiki = new Wikiapi('en');
console.assert(wiki.site_name() === 'enwiki');
console.log(wiki.site_name({ get_all_properties: true }));
console.assert(wiki.site_name({ get_all_properties: true }).language === 'en');
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_site_name(language, options) {
return wiki_API.site_name(language, options);
}
Wikiapi.site_name = Wikiapi_site_name;
// --------------------------------------------------------
// administration functions 管理功能。
/**
* @alias delete
* @description delete page
*
* @param {String} title - page title
* @param {Object} [options] - options to run this function
*
* @returns {Promise} Promise object represents response of delete.
*
* @example
const testwiki = new Wikiapi('test');
await testwiki.delete('Page to delete', { reason: 'test' });
// { title: 'Aaaaaaa', reason: 'test', logid: 346223 }
//
*
* @memberof Wikiapi.prototype
*/
function Wikiapi_delete(title, options) {
function Wikiapi_delete_executor(resolve, reject) {
const wiki = this[KEY_wiki_session];
// using wiki_API.delete
wiki.page(title).delete(options, (response, error) => {
if (error) {
reject(error);
} else {
resolve(response);
}
}, options);
}
return new Promise(Wikiapi_delete_executor.bind(this));
}
// --------------------------------------------------------
// exports
Object.assign(Wikiapi.prototype, {
append_session_to_options(options) {
// Object.assign({ [KEY_SESSION]: wiki }, options)
// return { ...options, [KEY_SESSION]: this[KEY_wiki_session] };
return wiki_API.add_session_to_options(this[KEY_wiki_session], options);
},
site_name(options) {
return Wikiapi_site_name(this[KEY_wiki_session], options);
},
login: Wikiapi_login,
query: Wikiapi_query,
page: Wikiapi_page,
tracking_revisions: Wikiapi_tracking_revisions,
edit_page: Wikiapi_edit_page,
/**
* @description edits content of target page.
const wiki = new Wikiapi;
wiki.listen(function for_each_row() {
// ...
}, {
// 檢查的延遲時間。
delay: '2m',
filter: function filter_row(row) {
// row is the same format as page_data
},
// also get diff
with_diff: { LCS: true, line: true },
// only for articles (0:main namespace) and talk pages
namespace: '0|talk',
});
//
*
* @memberof Wikiapi.prototype
*/
listen(listener, options) {
const wiki = this[KEY_wiki_session];
wiki.listen(listener, options);
},
category_tree: Wikiapi_category_tree,
search: Wikiapi_search,
redirects_root: Wikiapi_redirects_root,
// Warning: 採用 wiki_API.redirects_here(title) 才能追溯重新導向的標的。
// wiki.redirects() 無法追溯重新導向的標的!
redirects_here: Wikiapi_redirects_here,
register_redirects: Wikiapi_register_redirects,
upload: Wikiapi_upload,
download: Wikiapi_download,
get_featured_content: Wikiapi_get_featured_content,
for_each_page: Wikiapi_for_each_page,
for_each: Wikiapi_for_each,
delete: Wikiapi_delete,
data: Wikiapi_data,
new_data_entity: Wikiapi_new_data_entity,
SPARQL: Wikiapi_SPARQL,
convert_Chinese: Wikiapi_convert_Chinese,
run_SQL: Wikiapi_run_SQL,
setup_layout_elements: Wikiapi_setup_layout_elements,
});
// wrapper for properties
for (const property_name of ('task_configuration|latest_task_configuration').split('|')) {
Object.defineProperty(Wikiapi.prototype, property_name, {
get() {
const wiki = this[KEY_wiki_session];
return wiki[property_name];
}
});
}
// wrapper for sync functions
for (const function_name of ('namespace|remove_namespace|is_namespace|to_namespace|is_talk_namespace|to_talk_page|talk_page_to_main|normalize_title|redirect_target_of|aliases_of_page|is_template'
// CeL.run('application.net.wiki.featured_content');
// [].map(wiki.to_talk_page.bind(wiki))
+ '|get_featured_content_configurations').split('|')) {
Wikiapi.prototype[function_name] = function wrapper() {
const wiki = this[KEY_wiki_session];
return wiki[function_name].apply(wiki, arguments);
};
}
// @see get_list.type @
// https://github.com/kanasimi/CeJS/blob/master/application/net/wiki/list.js
for (const type of wiki_API.list.type_list) {
// Cannot use `= (title, options) {}` !
// arrow function expression DO NOT has this, arguments, super, or
// new.target keywords.
Wikiapi.prototype[type] = function (title, options) {
const _this = this;
/**
* @example
const page_list = await wiki.embeddedin(template_name, options);
await page_list.each((page_data) => { }, options);
*
*/
return Wikiapi_list.call(this, type, title, options)
.then((page_list) => {
// console.log(page_list);
page_list.each = Wikiapi_for_each_page.bind(_this, page_list);
return page_list;
});
};
}
module.exports = Wikiapi;
// export default Wikiapi;