%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;