UNPKG

cejs

Version:

A JavaScript module framework that is simple to use.

326 lines (270 loc) 9.1 kB
/** * @name CeL function for MediaWiki (Wikipedia / 維基百科): Page * * @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。 * * TODO:<code> </code> * * @since * * @see https://mwn.toolforge.org/docs/interfaces/_page_.mwnpage.html */ // More examples: see /_test suite/test.js // Wikipedia bots demo: https://github.com/kanasimi/wikibot 'use strict'; // 'use asm'; // -------------------------------------------------------------------------------------------- // 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。 typeof CeL === 'function' && CeL.run({ // module name name : 'application.net.wiki.page.Page', require : 'data.code.compatibility.' // + '|application.net.wiki.page.' // + '|application.net.wiki.list.', // 設定不匯出的子函式。 no_extend : 'this,*', // 為了方便格式化程式碼,因此將 module 函式主體另外抽出。 code : module_code }); function module_code(library_namespace) { // requiring var wiki_API = library_namespace.application.net.wiki, KEY_SESSION = wiki_API.KEY_SESSION; // @inner // var get_namespace = wiki_API.namespace; // ------------------------------------------------------------------------ if (false) { // call new_Page() page = wiki_session.Page(page_title); // {Number}p.ns // {String}p.title // await page.backlinks({get_list:true}) will get {Array}list. // page.backlinks() is asyncIterator // // https://www.codementor.io/@tiagolopesferreira/asynchronous-iterators-in-javascript-jl1yg8la1 // https://stackoverflow.com/questions/55531247/using-javascripts-symbol-asynciterator-with-for-await-of-loop // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols // for await (const page_data of page.backlinks()) { // console.log(page_data); } // TODO: // typeof await(page.content() || page.read()) === 'string'; typeof page.wikitext === 'string'; // page.fetch() is asyncIterator 泛用方法 相當於 wiki.query() // page.revisions() is asyncIterator // typeof await(page.is_biography()) === 'boolean'; } function Page(page_title, options, session) { this[KEY_SESSION] = session; if (wiki_API.is_page_data(page_title)) { Object.assign(this, page_title); return; } // page_data 之 structure 按照 wiki API 本身之 return // page_data = {pageid,ns,title,revisions:[{revid,timestamp,'*'}]} Object.assign(this, { // pageid : 0, ns : session.namespace(page_title) || 0, title : session.normalize_title(page_title) }); } function set_options_session(options) { var session = this[KEY_SESSION]; options = wiki_API.add_session_to_options(session, options); return options; } // ------------------------------------------------------------------------ function Page__content(options) { options = set_options_session.call(this, options); if (this.revisions) { return wiki_API.content_of(this, options); } var promise = new Promise(function executor(resolve, reject) { wiki_API.page(wiki_API.is_page_data(this) ? this : this.title, // function(page_data, error) { if (error) { reject(error); return; } if (!page_data.revisions) { reject(new Error('No .revisions get!')); return; } Object.assign(this, page_data); // console.trace(this); return resolve(wiki_API.content_of(this, options)); }.bind(this), options); }.bind(this)); return promise; } // ------------------------------------------------------------------------ function Page__check_stop(options) { // Copy from wiki_API.prototype.next.edit var promise = new Promise(function executor(resolve, reject) { wiki_API.check_stop(function(task_status, error) { if (error) reject(error); else resolve(task_status); }, add_session_to_options(this, options)); }.bind(this)); return promise; } function Page__edit(content, options) { options = set_options_session.call(this, options); var session = this[KEY_SESSION]; var check_task_id = wiki_API.get_task_id(wiki_API .add_session_to_options(session, options)) || wiki_API.check_stop.KEY_any_task; // Copy from wiki_API.prototype.next.edit var promise = new Promise(function executor(resolve, reject) { if (session.task_control_status[check_task_id] && session.task_control_status[check_task_id].stopped && !options.skip_stopped) { library_namespace.warn('Page__edit: 已停止作業,放棄編輯' + wiki_API.title_link_of(this) + '!'); reject(new Error('放棄編輯')); return; } if (this.is_Flow) { reject(new Error(new Error('NYI: flow page'))); } if (options.skip_nochange // 採用 skip_nochange 可以跳過實際 edit 的動作。 && content === wiki_API.content_of(this)) { library_namespace.debug('Skip ' // + wiki_API.title_link_of(this) // 'nochange', no change + ': The same content.', 1, 'Page__edit'); resolve('The same content'); return; } // console.trace([ this, wiki_API.is_page_data(this), session.token // ]); wiki_API.edit([ session.API_URL, // wiki_API.is_page_data(this) ? this : this.title ], content, // session.token, options, function wiki_API_Page_edit_callback(title, error, result) { if (error) { reject(error); return; } resolve(result); }); }.bind(this)); if (!session.task_control_status[check_task_id]) { promise = Page__check_stop.call(this, options).then(promise); } if (typeof content === 'function') return this.content().then(promise); return promise; } // ------------------------------------------------------------------------ function Page__list(options) { options = set_options_session.call(this, options); // options.type, options[KEY_SESSION] are setted in Page__list_async() var promise = new Promise(function executor(resolve, reject) { wiki_API.list(wiki_API.is_page_data(this) ? this : this.title, // function(pages, target, options) { if (pages.error) reject(pages.error); else resolve(pages); }, options); }.bind(this)); return promise; } var Symbol_asyncIterator = typeof Symbol === 'function' && Symbol.asyncIterator; var done_object = { // value : generator.page_count, done : true }; function Page__list_async(method, options) { options = set_options_session.call(this, options); options.type = method; if (!Symbol_asyncIterator || options && options.get_list) { return Page__list.call(this, options); } // -------------------------------------- var list_generator = Object.create(null); list_generator[Symbol_asyncIterator] = (function() { function get_next_object() { return { value : generator.queue.shift(), done : false }; } var generator = { queue : [], next : function() { if (generator.resolve) { throw new Error( 'Call resolve() before latest promise resolved'); } if (generator.queue.length > 0) { // 執行順序3: 中間最多的是這個程序一直反覆 loop return Promise.resolve(get_next_object()); } // assert: generator.queue.length === 0 if (generator.done) { // 執行順序4: 最後一次 iterate return Promise.resolve(done_object); } // 執行順序1 return new Promise(function(resolve, reject) { generator.resolve = resolve; }); } }; options.for_each_page = function(item) { generator.queue.push(item); var resolve = generator.resolve; if (resolve) { delete generator.resolve; // 執行順序2 resolve(get_next_object()); } }; wiki_API.list(wiki_API.is_page_data(this) ? this : this.title, // function(list) { // generator.page_count = list.length; generator.done = true; var resolve = generator.resolve; if (resolve) { // 基本上不會執行到這邊 @ node.js delete generator.resolve; resolve(done_object); } }, options); return generator; }).bind(this); return list_generator; } // ------------------------------------------------------------------------ // export 導出. Object.assign(wiki_API.prototype, { new_Page : function new_Page(page_title, options) { return new Page(page_title, options,/* session */this); }, Page : Page }); wiki_API.list.type_list.forEach(function(method) { // if (!method.includes('all')) Page.prototype[method] = function Page__list_frontend(options) { return Page__list_async.call(this, method, options); }; }); Object.assign(Page.prototype, { content : Page__content, edit : Page__edit }); return Page; }