%PDF- %PDF-
Direktori : /www/varak.net/wiki.varak.net/extensions/MobileFrontend/src/mobile.startup/ |
Current File : /www/varak.net/wiki.varak.net/extensions/MobileFrontend/src/mobile.startup/PageGateway.js |
var sectionTemplate = mw.template.get( 'mobile.startup', 'Section.hogan' ), util = require( './util.js' ), cache = {}; /** * Add child to listOfSections if the level of child is the same as the last * child of listOfSections, otherwise add it to the children of the last * section of listOfSections. If listOfSections is empty, just add child to it. * @private * @param {Array} listOfSections - Array of section ids * @param {Object} child - Section to be added to listOfSections */ function assignToParent( listOfSections, child ) { var section; if ( listOfSections.length === 0 ) { listOfSections.push( child ); } else { // take a look at the last child section = listOfSections[listOfSections.length - 1]; // If the level is the same as another section in this list it is a sibling if ( parseInt( section.level, 10 ) === parseInt( child.level, 10 ) ) { listOfSections.push( child ); } else { // Otherwise take a look at that sections children recursively assignToParent( section.children, child ); } } } /** * Order sections hierarchically * @private * @param {Array} sections Array of section objects created from response HTML * @return {Array} Ordered array of sections */ function transformSections( sections ) { var sectionLevels = sections.map( function ( s ) { return s.level; } ), existingSectionLevels = sectionLevels.filter( function ( level ) { return !!level; } ), collapseLevel = Math.min.apply( this, existingSectionLevels ).toString(), lastSection, result = []; // if the first section level is not equal to collapseLevel, this first // section will not have a parent and will be appended to the result. sections.forEach( function ( section ) { if ( section.line !== undefined ) { section.line = section.line.replace( /<\/?a\b[^>]*>/g, '' ); } section.children = []; if ( !lastSection || ( !section.level || section.level === collapseLevel ) || // make sure lastSections first child's level is bigger than section.level ( lastSection.children.length && lastSection.children[0].level > section.level ) || // also make sure section.level is not bigger than the lastSection.level ( lastSection.level && lastSection.level >= section.level ) ) { result.push( section ); lastSection = section; } else { assignToParent( lastSection.children, section ); lastSection.text += sectionTemplate.render( section ); } } ); return result; } /** * API for providing Page data * @class PageGateway * @param {mw.Api} api */ function PageGateway( api ) { this.api = api; } PageGateway.prototype = { /** * Retrieve a page from the api * @memberof PageGateway * @instance * @param {string} title the title of the page to be retrieved * @param {string} endpoint an alternative api url to retrieve the page from * @param {boolean} leadOnly When set only the lead section content is returned * @return {jQuery.Deferred} with parameter page data that can be passed to a Page view */ getPage: function ( title, endpoint, leadOnly ) { var timestamp, d = util.Deferred(), options = endpoint ? { url: endpoint, dataType: 'jsonp' } : {}, protection = { edit: [ '*' ] }; if ( !cache[title] ) { cache[title] = this.api.get( { action: 'mobileview', page: title, variant: mw.config.get( 'wgPageContentLanguage' ), redirect: 'yes', prop: 'id|sections|text|lastmodified|lastmodifiedby|languagecount|hasvariants|protection|displaytitle|revision', noheadings: 'yes', sectionprop: 'level|line|anchor', sections: leadOnly ? 0 : 'all' }, options ).then( function ( resp ) { var sections, lastModified, resolveObj, mv; if ( resp.error ) { return d.reject( resp.error ); } else if ( !resp.mobileview.sections ) { return d.reject( 'No sections' ); } else { mv = resp.mobileview; sections = transformSections( mv.sections ); // Assume the timestamp is in the form TS_ISO_8601 // and we don't care about old browsers // change to seconds to be consistent with PHP timestamp = new Date( mv.lastmodified ).getTime() / 1000; lastModified = mv.lastmodifiedby; // FIXME: [API] the API sometimes returns an object and sometimes an array // There are various quirks with the format of protection level // as returned by api. // Also it is usually incomplete - if something is missing this means // that it has no protection level. // When an array this means there is no protection level set. // To keep the data type consistent either use the predefined // protection level, // or extend it with what is returned by API. protection = Array.isArray( mv.protection ) ? protection : util.extend( protection, mv.protection ); resolveObj = { title: title, id: mv.id, revId: mv.revId, protection: protection, lead: sections[0].text, sections: sections.slice( 1 ), isMainPage: mv.mainpage !== undefined, historyUrl: mw.util.getUrl( title, { action: 'history' } ), lastModifiedTimestamp: timestamp, languageCount: mv.languagecount, hasVariants: mv.hasvariants !== undefined, displayTitle: mv.displaytitle }; // Add non-anonymous user information if ( lastModified ) { util.extend( resolveObj, { lastModifiedUserName: lastModified.name, lastModifiedUserGender: lastModified.gender } ); } // FIXME: Return a Page class here return resolveObj; } }, function ( msg ) { return d.reject( msg ); } ); } return cache[title]; }, /** * Invalidate the internal cache for a given page * @memberof PageGateway * @instance * @param {string} title the title of the page who's cache you want to invalidate */ invalidatePage: function ( title ) { delete cache[title]; }, /** * Gets language variant list for a page; helper function for getPageLanguages() * @memberof PageGateway * @instance * @private * @param {string} title Name of the page to obtain variants for * @param {Object} data Data from API * @return {Array|boolean} List of language variant objects or false if no variants exist */ _getLanguageVariantsFromApiResponse: function ( title, data ) { var generalData = data.query.general, variantPath = generalData.variantarticlepath, variants = []; if ( !generalData.variants ) { return false; } // Create the data object for each variant and store it Object.keys( generalData.variants ).forEach( function ( index ) { var item = generalData.variants[ index ], variant = { autonym: item.name, lang: item.code }; if ( variantPath ) { variant.url = variantPath .replace( '$1', title ) .replace( '$2', item.code ); } else { variant.url = mw.util.getUrl( title, { variant: item.code } ); } variants.push( variant ); } ); return variants; }, /** * Retrieve available languages for a given title * @memberof PageGateway * @instance * @param {string} title the title of the page languages should be retrieved for * @param {string} [language] when provided the names of the languages returned * will be translated additionally into this language. * @return {jQuery.Deferred} which is called with an object containing langlinks * and variant links as defined @ https://en.m.wikipedia.org/w/api.php?action=help&modules=query%2Blanglinks */ getPageLanguages: function ( title, language ) { var self = this, args = { action: 'query', meta: 'siteinfo', siprop: 'general', prop: 'langlinks', lllimit: 'max', titles: title, formatversion: 2 }; if ( language ) { args.llprop = 'url|autonym|langname'; args.llinlanguagecode = language; } else { args.llprop = 'url|autonym'; } return this.api.get( args ).then( function ( resp ) { return { languages: resp.query.pages[0].langlinks || [], variants: self._getLanguageVariantsFromApiResponse( title, resp ) }; }, function () { return util.Deferred().reject(); } ); }, /** * Extract sections from headings in $el * @memberof PageGateway * @instance * @private * @param {jQuery.Object} $el object from which sections are extracted * @return {Array} Array of section objects created from headings in $el * FIXME: Where's a better place for getSectionsFromHTML and this function to live? * Answer: Possibly Page.js */ _getAPIResponseFromHTML: function ( $el ) { // FIXME: use Page.HEADING_SELECTOR var $headings = $el.find( 'h1,h2,h3,h4,h5,h6' ), sections = []; $headings.each( function () { var level = this.tagName.substr( 1 ), $span = $el.find( this ).find( '.mw-headline' ); if ( $span.length ) { sections.push( { level: level, line: $span.html(), anchor: $span.attr( 'id' ) || '', text: '' } ); } } ); return sections; }, /** * Order sections hierarchically * @memberof PageGateway * @instance * @param {jQuery.Object} $el object from which sections are extracted * @return {Array} Ordered array of sections */ getSectionsFromHTML: function ( $el ) { return transformSections( this._getAPIResponseFromHTML( $el ) ); } }; module.exports = PageGateway;