mirror of
				https://scm.univ-tours.fr/22107988t/rappaurio-sae501_502.git
				synced 2025-11-04 09:15:21 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			321 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			321 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'use strict';
 | 
						|
import fetch from 'cross-fetch';
 | 
						|
import querystring from 'querystring';
 | 
						|
import { pagination, api, aggregate } from './util.js';
 | 
						|
import wikiPage from './page.js';
 | 
						|
import QueryChain from './chain.js';
 | 
						|
/**
 | 
						|
 * @namespace
 | 
						|
 * @constant
 | 
						|
 * @property {string} apiUrl - URL of Wikipedia API
 | 
						|
 * @property {string} headers - Headers to pass through to the API request
 | 
						|
 * @property {string} origin - When accessing the API using a cross-domain AJAX
 | 
						|
 * request (CORS), set this to the originating domain. This must be included in
 | 
						|
 * any pre-flight request, and therefore must be part of the request URI (not
 | 
						|
 * the POST body). This must match one of the origins in the Origin header
 | 
						|
 * exactly, so it has to be set to something like https://en.wikipedia.org or
 | 
						|
 * https://meta.wikimedia.org. If this parameter does not match the Origin
 | 
						|
 * header, a 403 response will be returned. If this parameter matches the Origin
 | 
						|
 * header and the origin is whitelisted, an Access-Control-Allow-Origin header
 | 
						|
 * will be set.
 | 
						|
 */
 | 
						|
const defaultOptions = {
 | 
						|
    apiUrl: 'http://en.wikipedia.org/w/api.php',
 | 
						|
    origin: '*'
 | 
						|
};
 | 
						|
/**
 | 
						|
 * wiki
 | 
						|
 * @example
 | 
						|
 * wiki({ apiUrl: 'http://fr.wikipedia.org/w/api.php' }).search(...);
 | 
						|
 * @namespace Wiki
 | 
						|
 * @param  {Object} options
 | 
						|
 * @return {Object} - wiki (for chaining methods)
 | 
						|
 */
 | 
						|
export default function wiki(options = {}) {
 | 
						|
    if (this instanceof wiki) {
 | 
						|
        // eslint-disable-next-line
 | 
						|
        console.log('Please do not use wikijs ^1.0.0 as a class. Please see the new README.');
 | 
						|
    }
 | 
						|
    const apiOptions = Object.assign({}, defaultOptions, options);
 | 
						|
    function handleRedirect(res) {
 | 
						|
        if (res.query.redirects && res.query.redirects.length === 1) {
 | 
						|
            return api(apiOptions, {
 | 
						|
                prop: 'info|pageprops',
 | 
						|
                inprop: 'url',
 | 
						|
                ppprop: 'disambiguation',
 | 
						|
                titles: res.query.redirects[0].to
 | 
						|
            });
 | 
						|
        }
 | 
						|
        return res;
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Search articles
 | 
						|
     * @example
 | 
						|
     * wiki.search('star wars').then(data => console.log(data.results.length));
 | 
						|
     * @example
 | 
						|
     * wiki.search('star wars').then(data => {
 | 
						|
     * 	data.next().then(...);
 | 
						|
     * });
 | 
						|
     * @method Wiki#search
 | 
						|
     * @param  {string} query - keyword query
 | 
						|
     * @param  {Number} [limit] - limits the number of results
 | 
						|
     * @param  {Boolean} [all] - returns entire article objects instead of just titles
 | 
						|
     * @return {Promise} - pagination promise with results and next page function
 | 
						|
     */
 | 
						|
    function search(query, limit = 50, all = false) {
 | 
						|
        return pagination(apiOptions, {
 | 
						|
            list: 'search',
 | 
						|
            srsearch: query,
 | 
						|
            srlimit: limit
 | 
						|
        }, res => res.query.search.map(article => {
 | 
						|
            return all ? article : article.title;
 | 
						|
        })).catch(err => {
 | 
						|
            if (err.message === '"text" search is disabled.') {
 | 
						|
                // Try backup search method
 | 
						|
                return opensearch(query, limit);
 | 
						|
            }
 | 
						|
            throw err;
 | 
						|
        });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Search articles using "fuzzy" prefixsearch
 | 
						|
     * @example
 | 
						|
     * wiki.prefixSearch('star wars').then(data => console.log(data.results.length));
 | 
						|
     * @example
 | 
						|
     * wiki.prefixSearch('star wars').then(data => {
 | 
						|
     * 	data.next().then(...);
 | 
						|
     * });
 | 
						|
     * @method Wiki#prefixSearch
 | 
						|
     * @param  {string} query - keyword query
 | 
						|
     * @param  {Number} [limit] - limits the number of results
 | 
						|
     * @return {Promise} - pagination promise with results and next page function
 | 
						|
     */
 | 
						|
    function prefixSearch(query, limit = 50) {
 | 
						|
        return pagination(apiOptions, {
 | 
						|
            list: 'prefixsearch',
 | 
						|
            pslimit: limit,
 | 
						|
            psprofile: 'fuzzy',
 | 
						|
            pssearch: query
 | 
						|
        }, res => res.query.prefixsearch.map(article => article.title));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Opensearch (mainly used as a backup to normal text search)
 | 
						|
     * @param  {string} query - keyword query
 | 
						|
     * @param  {Number} limit - limits the number of results
 | 
						|
     * @return {Array}       List of page title results
 | 
						|
     */
 | 
						|
    function opensearch(query, limit = 50) {
 | 
						|
        return api(apiOptions, {
 | 
						|
            search: query,
 | 
						|
            limit,
 | 
						|
            namespace: 0,
 | 
						|
            action: 'opensearch',
 | 
						|
            redirects: undefined
 | 
						|
        }).then(res => res[1]);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Random articles
 | 
						|
     * @example
 | 
						|
     * wiki.random(3).then(results => console.log(results[0]));
 | 
						|
     * @method Wiki#random
 | 
						|
     * @param  {Number} [limit] - limits the number of random articles
 | 
						|
     * @return {Promise} - List of page titles
 | 
						|
     */
 | 
						|
    function random(limit = 1) {
 | 
						|
        return api(apiOptions, {
 | 
						|
            list: 'random',
 | 
						|
            rnnamespace: 0,
 | 
						|
            rnlimit: limit
 | 
						|
        }).then(res => res.query.random.map(article => article.title));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get Page
 | 
						|
     * @example
 | 
						|
     * wiki.page('Batman').then(page => console.log(page.pageid));
 | 
						|
     * @method Wiki#page
 | 
						|
     * @param  {string} title - title of article
 | 
						|
     * @return {Promise}
 | 
						|
     */
 | 
						|
    function page(title) {
 | 
						|
        return api(apiOptions, {
 | 
						|
            prop: 'info|pageprops',
 | 
						|
            inprop: 'url',
 | 
						|
            ppprop: 'disambiguation',
 | 
						|
            titles: title
 | 
						|
        })
 | 
						|
            .then(handleRedirect)
 | 
						|
            .then(res => {
 | 
						|
            const id = Object.keys(res.query.pages)[0];
 | 
						|
            if (!id || id === '-1') {
 | 
						|
                throw new Error('No article found');
 | 
						|
            }
 | 
						|
            return wikiPage(res.query.pages[id], apiOptions);
 | 
						|
        });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Get Page by PageId
 | 
						|
     * @example
 | 
						|
     * wiki.findById(4335).then(page => console.log(page.title));
 | 
						|
     * @method Wiki#findById
 | 
						|
     * @param {integer} pageid, id of the page
 | 
						|
     * @return {Promise}
 | 
						|
     */
 | 
						|
    function findById(pageid) {
 | 
						|
        return api(apiOptions, {
 | 
						|
            prop: 'info|pageprops',
 | 
						|
            inprop: 'url',
 | 
						|
            ppprop: 'disambiguation',
 | 
						|
            pageids: pageid
 | 
						|
        })
 | 
						|
            .then(handleRedirect)
 | 
						|
            .then(res => {
 | 
						|
            const id = Object.keys(res.query.pages)[0];
 | 
						|
            if (!id || id === '-1') {
 | 
						|
                throw new Error('No article found');
 | 
						|
            }
 | 
						|
            return wikiPage(res.query.pages[id], apiOptions);
 | 
						|
        });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Find page by query and optional predicate
 | 
						|
     * @example
 | 
						|
     * wiki.find('luke skywalker').then(page => console.log(page.title));
 | 
						|
     * @method Wiki#find
 | 
						|
     * @param {string} search query
 | 
						|
     * @param {function} [predicate] - testing function for choosing which page result to fetch. Default is first result.
 | 
						|
     * @return {Promise}
 | 
						|
     */
 | 
						|
    function find(query, predicate = results => results[0]) {
 | 
						|
        return search(query)
 | 
						|
            .then(res => predicate(res.results))
 | 
						|
            .then(name => page(name));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Geographical Search
 | 
						|
     * @example
 | 
						|
     * wiki.geoSearch(32.329, -96.136).then(titles => console.log(titles.length));
 | 
						|
     * @method Wiki#geoSearch
 | 
						|
     * @param  {Number} lat - latitude
 | 
						|
     * @param  {Number} lon - longitude
 | 
						|
     * @param  {Number} [radius=1000] - search radius in meters (default: 1km)
 | 
						|
     * @param  {Number} [limit=10] - number of results (default: 10 results)
 | 
						|
     * @return {Promise} - List of page titles
 | 
						|
     */
 | 
						|
    function geoSearch(lat, lon, radius = 1000, limit = 10) {
 | 
						|
        return api(apiOptions, {
 | 
						|
            list: 'geosearch',
 | 
						|
            gsradius: radius,
 | 
						|
            gscoord: `${lat}|${lon}`,
 | 
						|
            gslimit: limit
 | 
						|
        }).then(res => res.query.geosearch.map(article => article.title));
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @summary Find the most viewed pages with counts
 | 
						|
     * @example
 | 
						|
     * wiki.mostViewed().then(list => console.log(`${list[0].title}: ${list[0].count}`))
 | 
						|
     * @method Wiki#mostViewed
 | 
						|
     * @returns {Promise} - Array of {title,count}
 | 
						|
     */
 | 
						|
    function mostViewed() {
 | 
						|
        return api(apiOptions, {
 | 
						|
            list: 'mostviewed'
 | 
						|
        }).then(res => {
 | 
						|
            return res.query.mostviewed.map(({ title, count }) => ({ title, count }));
 | 
						|
        });
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Fetch all page titles in wiki
 | 
						|
     * @method Wiki#allPages
 | 
						|
     * @return {Array} Array of pages
 | 
						|
     */
 | 
						|
    function allPages() {
 | 
						|
        return aggregate(apiOptions, {}, 'allpages', 'title', 'ap');
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Fetch all categories in wiki
 | 
						|
     * @method Wiki#allCategories
 | 
						|
     * @return {Array} Array of categories
 | 
						|
     */
 | 
						|
    function allCategories() {
 | 
						|
        return aggregate(apiOptions, {}, 'allcategories', '*', 'ac');
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * Fetch all pages in category
 | 
						|
     * @method Wiki#pagesInCategory
 | 
						|
     * @param  {String} category Category to fetch from
 | 
						|
     * @return {Array} Array of pages
 | 
						|
     */
 | 
						|
    function pagesInCategory(category) {
 | 
						|
        return aggregate(apiOptions, {
 | 
						|
            cmtitle: category
 | 
						|
        }, 'categorymembers', 'title', 'cm');
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @summary Helper function to query API directly
 | 
						|
     * @method Wiki#api
 | 
						|
     * @param {Object} params [https://www.mediawiki.org/wiki/API:Query](https://www.mediawiki.org/wiki/API:Query)
 | 
						|
     * @returns {Promise} Query Response
 | 
						|
     * @example
 | 
						|
     * wiki().api({
 | 
						|
     *	action: 'parse',
 | 
						|
     *	page: 'Pet_door'
 | 
						|
     * }).then(res => res.parse.title.should.equal('Pet door'));
 | 
						|
     */
 | 
						|
    function rawApi(params) {
 | 
						|
        return api(apiOptions, params);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @summary Returns a QueryChain to efficiently query specific data
 | 
						|
     * @method Wiki#chain
 | 
						|
     * @returns {QueryChain}
 | 
						|
     * @example
 | 
						|
     * // Find summaries and images of places near a specific location
 | 
						|
     * wiki()
 | 
						|
     *	.chain()
 | 
						|
     *	.geosearch(52.52437, 13.41053)
 | 
						|
     *	.summary()
 | 
						|
     *	.image()
 | 
						|
     *	.coordinates()
 | 
						|
     *	.request()
 | 
						|
     */
 | 
						|
    function chain() {
 | 
						|
        return new QueryChain(apiOptions);
 | 
						|
    }
 | 
						|
    /**
 | 
						|
     * @summary Returns the Export XML for a page to be used for importing into another MediaWiki
 | 
						|
     * @method Wiki#exportXml
 | 
						|
     * @param {string} pageName
 | 
						|
     * @returns {Promise<string>} Export XML
 | 
						|
     */
 | 
						|
    function exportXml(pageName) {
 | 
						|
        const qs = {
 | 
						|
            title: 'Special:Export',
 | 
						|
            pages: pageName
 | 
						|
        };
 | 
						|
        // The replace here is kinda hacky since
 | 
						|
        // the export action does not use
 | 
						|
        // the normal api endpoint.
 | 
						|
        const url = `${apiOptions.apiUrl.replace('api', 'index')}?${querystring.stringify(qs)}`;
 | 
						|
        const headers = Object.assign({ 'User-Agent': 'WikiJS Bot v1.0' }, apiOptions.headers);
 | 
						|
        return fetch(url, { headers }).then(res => res.text());
 | 
						|
    }
 | 
						|
    return {
 | 
						|
        search,
 | 
						|
        random,
 | 
						|
        page,
 | 
						|
        geoSearch,
 | 
						|
        options,
 | 
						|
        findById,
 | 
						|
        find,
 | 
						|
        allPages,
 | 
						|
        allCategories,
 | 
						|
        pagesInCategory,
 | 
						|
        opensearch,
 | 
						|
        prefixSearch,
 | 
						|
        mostViewed,
 | 
						|
        api: rawApi,
 | 
						|
        chain,
 | 
						|
        exportXml
 | 
						|
    };
 | 
						|
}
 |