process inline styles
This commit is contained in:
parent
ee9aa4e6bd
commit
32d1d6642f
214
wikizimmer.js
214
wikizimmer.js
|
@ -334,12 +334,11 @@ class WikiItem {
|
||||||
Object.assign( this, { zimNameSpace, url, title })
|
Object.assign( this, { zimNameSpace, url, title })
|
||||||
}
|
}
|
||||||
|
|
||||||
data () {
|
async getData () {
|
||||||
return ( this.data_ !== undefined ? Promise.resolve( this.data_ ) : ( this.data_ = this.load( )))
|
let data = await ( this.data !== undefined ? Promise.resolve( this.data ) : ( this.data = this.load( )))
|
||||||
.then( data => ! Buffer.isBuffer( data ) || this.encoding == null
|
if ( Buffer.isBuffer( data ) && this.encoding != null )
|
||||||
? data
|
data = iconv.decode( data, this.encoding )
|
||||||
: iconv.decode( data, this.encoding )
|
return data
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
urlReplacements () {
|
urlReplacements () {
|
||||||
|
@ -412,6 +411,10 @@ class WikiItem {
|
||||||
return '/' + this.zimNameSpace + '/' + this.baseName()
|
return '/' + this.zimNameSpace + '/' + this.baseName()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rootPath () {
|
||||||
|
return '../'.repeat( this.baseName().split( '/' ).length - 1 )
|
||||||
|
}
|
||||||
|
|
||||||
urlKey () {
|
urlKey () {
|
||||||
return this.zimNameSpace + this.baseName()
|
return this.zimNameSpace + this.baseName()
|
||||||
}
|
}
|
||||||
|
@ -463,7 +466,7 @@ class WikiItem {
|
||||||
|
|
||||||
process () {
|
process () {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then( () => this.data())
|
.then( () => this.getData())
|
||||||
.then( data => this.store( data ))
|
.then( data => this.store( data ))
|
||||||
.then( () => this.storeMetadata() )
|
.then( () => this.storeMetadata() )
|
||||||
.then( () => this.localPath() )
|
.then( () => this.localPath() )
|
||||||
|
@ -513,7 +516,6 @@ class ArticleStub extends WikiItem {
|
||||||
class Article extends ArticleStub {
|
class Article extends ArticleStub {
|
||||||
constructor ( pageInfo ) {
|
constructor ( pageInfo ) {
|
||||||
super( pageInfo )
|
super( pageInfo )
|
||||||
this.basePath = '../'.repeat( this.baseName().split( '/' ).length - 1 )
|
|
||||||
}
|
}
|
||||||
|
|
||||||
load () {
|
load () {
|
||||||
|
@ -521,7 +523,7 @@ class Article extends ArticleStub {
|
||||||
.then( body => this.preProcess( body ))
|
.then( body => this.preProcess( body ))
|
||||||
}
|
}
|
||||||
|
|
||||||
preProcess( data, reply ) {
|
async preProcess( data, reply ) {
|
||||||
let src
|
let src
|
||||||
try {
|
try {
|
||||||
src = cheerio.load( data )
|
src = cheerio.load( data )
|
||||||
|
@ -529,56 +531,61 @@ class Article extends ArticleStub {
|
||||||
log( 'cheerio.load error', e, data, reply )
|
log( 'cheerio.load error', e, data, reply )
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
let content = src( '#bodyContent' )
|
try {
|
||||||
if ( content.length == 0 ) {
|
let content = src( '#bodyContent' )
|
||||||
content = src( 'article' )
|
if ( content.length == 0 ) {
|
||||||
}
|
content = src( 'article' )
|
||||||
if ( content.length == 0 ) {
|
}
|
||||||
fatal( "Article.preProcess -- fatal error: Can't find article's content:", this.title )
|
if ( content.length == 0 ) {
|
||||||
}
|
fatal( "Article.preProcess -- fatal error: Can't find article's content:", this.title )
|
||||||
|
|
||||||
const dom = cheerio.load( wiki.pageTemplate )
|
|
||||||
dom( 'title' ).text( this.title )
|
|
||||||
|
|
||||||
dom( '#bodyContent' ).replaceWith( content )
|
|
||||||
|
|
||||||
// remove comments
|
|
||||||
dom( '*' ).contents().each( (i, elem) => {
|
|
||||||
//~ log( 'comment', elem.type )
|
|
||||||
if ( elem.type === 'comment' ) {
|
|
||||||
dom( elem ).remove()
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
// display content inside <noscript> tags
|
const dom = cheerio.load( wiki.pageTemplate )
|
||||||
dom( 'noscript' ).each( (i, elem) => {
|
dom( 'title' ).text( this.title )
|
||||||
let e = dom( elem )
|
|
||||||
e.replaceWith( e.contents() )
|
|
||||||
})
|
|
||||||
|
|
||||||
// modify links
|
dom( '#bodyContent' ).replaceWith( content )
|
||||||
let css = dom( '#layout-css' )
|
|
||||||
css.attr( 'href', this.basePath + css.attr( 'href' ))
|
|
||||||
|
|
||||||
dom( 'a' ).each( (i, elem) => {
|
// remove comments
|
||||||
this.transformGeoLink( elem )
|
dom( '*' ).contents().each( (i, elem) => {
|
||||||
this.transformLink( elem )
|
//~ log( 'comment', elem.type )
|
||||||
})
|
if ( elem.type === 'comment' ) {
|
||||||
// map area links
|
dom( elem ).remove()
|
||||||
dom( 'area' ).each( (i, elem) => {
|
}
|
||||||
this.transformLink( elem )
|
})
|
||||||
})
|
|
||||||
|
// display content inside <noscript> tags
|
||||||
|
dom( 'noscript' ).each( (i, elem) => {
|
||||||
|
let e = dom( elem )
|
||||||
|
e.replaceWith( e.contents() )
|
||||||
|
})
|
||||||
|
|
||||||
|
// modify links
|
||||||
|
let css = dom( '#layout-css' )
|
||||||
|
css.attr( 'href', this.rootPath() + css.attr( 'href' ))
|
||||||
|
|
||||||
|
dom( 'a' ).each( (i, elem) => {
|
||||||
|
this.transformGeoLink( elem )
|
||||||
|
this.transformLink( elem )
|
||||||
|
})
|
||||||
|
// map area links
|
||||||
|
dom( 'area' ).each( (i, elem) => {
|
||||||
|
this.transformLink( elem )
|
||||||
|
})
|
||||||
|
|
||||||
|
let done = dom( 'img' ).toArray().map( elem => this.transformImg( elem ))
|
||||||
|
done = done.concat( dom( '[style*="url("]' ).toArray().map( elem => this.transformStyle( elem )))
|
||||||
|
|
||||||
|
await Promise.all( done )
|
||||||
|
|
||||||
return Promise.all( dom( 'img' ).toArray().map( elem => this.saveImage( elem )))
|
|
||||||
.then ( () => {
|
|
||||||
this.mimeType = 'text/html'
|
this.mimeType = 'text/html'
|
||||||
this.encoding = 'utf-8'
|
this.encoding = 'utf-8'
|
||||||
const out = dom.html()
|
const out = dom.html()
|
||||||
return out
|
return out
|
||||||
})
|
|
||||||
.catch( err => {
|
} catch ( err ) {
|
||||||
log( err )
|
log( err )
|
||||||
})
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transformLink( elem ) {
|
transformLink( elem ) {
|
||||||
|
@ -601,7 +608,7 @@ class Article extends ArticleStub {
|
||||||
if ( path.includes( ':' )) {
|
if ( path.includes( ':' )) {
|
||||||
delete elem.attribs.href // block other name spaces
|
delete elem.attribs.href // block other name spaces
|
||||||
} else {
|
} else {
|
||||||
elem.attribs.href = this.basePath + baseName
|
elem.attribs.href = this.rootPath() + baseName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const pathlc = path.toLowerCase()
|
const pathlc = path.toLowerCase()
|
||||||
|
@ -621,16 +628,23 @@ class Article extends ArticleStub {
|
||||||
elem.attribs.href = `geo:${lat},${lon}`
|
elem.attribs.href = `geo:${lat},${lon}`
|
||||||
}
|
}
|
||||||
|
|
||||||
saveImage ( elem ) {
|
async transformStyle ( elem ) {
|
||||||
|
let style = new Style( this.url, elem.attribs.style )
|
||||||
|
return elem.attribs.style = await style.getData()
|
||||||
|
}
|
||||||
|
|
||||||
|
async transformImg ( elem ) {
|
||||||
delete elem.attribs.srcset
|
delete elem.attribs.srcset
|
||||||
let url = elem.attribs.src
|
let url = elem.attribs.src
|
||||||
if (! url || url.startsWith( 'data:' ))
|
if (! url || url.startsWith( 'data:' ))
|
||||||
return url
|
return url
|
||||||
|
return elem.attribs.src = await this.saveImage( url )
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveImage ( url ) {
|
||||||
const image = new Image( url )
|
const image = new Image( url )
|
||||||
return image.process()
|
const localPath = await image.process()
|
||||||
.then( localPath => {
|
return encodeURI( this.rootPath() + '..' + localPath )
|
||||||
elem.attribs.src = encodeURI( this.basePath + '..' + localPath )
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,10 +714,10 @@ class Metadata extends WikiItem {
|
||||||
constructor ( url, data ) {
|
constructor ( url, data ) {
|
||||||
super( 'M', url)
|
super( 'M', url)
|
||||||
this.mimeType = 'text/plain'
|
this.mimeType = 'text/plain'
|
||||||
this.data_ = data
|
this.data = data
|
||||||
}
|
}
|
||||||
data () {
|
getData () {
|
||||||
return this.data_
|
return this.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -748,7 +762,7 @@ class Image extends PageComponent {
|
||||||
data () {
|
data () {
|
||||||
if (! command.images )
|
if (! command.images )
|
||||||
return null
|
return null
|
||||||
return super.data()
|
return super.getData()
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
process () {
|
process () {
|
||||||
|
@ -760,7 +774,7 @@ class Image extends PageComponent {
|
||||||
|
|
||||||
//~ const layoutFileNames = new Set()
|
//~ const layoutFileNames = new Set()
|
||||||
|
|
||||||
class StyleItem extends PageComponent {
|
class LayoutItem extends PageComponent {
|
||||||
constructor ( url ) {
|
constructor ( url ) {
|
||||||
super( '-', url )
|
super( '-', url )
|
||||||
}
|
}
|
||||||
|
@ -777,7 +791,7 @@ class StyleItem extends PageComponent {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
class FavIcon extends StyleItem {
|
class FavIcon extends LayoutItem {
|
||||||
constructor ( ) {
|
constructor ( ) {
|
||||||
super( wiki.info.general.logo || 'http://www.openzim.org/w/images/e/e8/OpenZIM-wiki.png' )
|
super( wiki.info.general.logo || 'http://www.openzim.org/w/images/e/e8/OpenZIM-wiki.png' )
|
||||||
}
|
}
|
||||||
|
@ -786,55 +800,79 @@ class FavIcon extends StyleItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Style extends LayoutItem {
|
||||||
|
constructor ( url, data ) {
|
||||||
|
super( url )
|
||||||
|
this.mimeType = 'text/css'
|
||||||
|
this.data = data
|
||||||
|
}
|
||||||
|
|
||||||
|
async getData () {
|
||||||
|
try {
|
||||||
|
const src = await super.getData()
|
||||||
|
return await this.transformStyle( src )
|
||||||
|
} catch ( err ) {
|
||||||
|
log( 'Style.getData error', this.url, err )
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async transformStyle ( src ) {
|
||||||
|
// collect urls using dummy replacements
|
||||||
|
const urlre = /(url\(['"]?)([^\)]*[^\)'"])(['"]?\))/g
|
||||||
|
const requests = []
|
||||||
|
src.replace( urlre, ( match, start, url, end ) => {
|
||||||
|
if ( ! url.startsWith( 'data:' )) {
|
||||||
|
const styleItem = new LayoutItem( urlconv.resolve( this.url, url ))
|
||||||
|
requests.push( styleItem.process() )
|
||||||
|
}
|
||||||
|
return match
|
||||||
|
})
|
||||||
|
const resolvedUrls = await Promise.all( requests )
|
||||||
|
const transformed = src.replace( urlre, ( match, start, url, end ) => {
|
||||||
|
let out = match
|
||||||
|
const rurl = resolvedUrls.shift()
|
||||||
|
if ( rurl != null ) {
|
||||||
|
let newUrl = this.rootPath() + '..' + rurl
|
||||||
|
out = start + newUrl + end
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
})
|
||||||
|
return transformed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const cssDependencies = new Set()
|
const cssDependencies = new Set()
|
||||||
|
|
||||||
class GlobalCss extends StyleItem {
|
class GlobalCss extends LayoutItem {
|
||||||
constructor ( sourceDOM ) {
|
constructor ( sourceDOM ) {
|
||||||
super( 'zim.css' )
|
super( 'zim.css' )
|
||||||
this.sourceDOM = sourceDOM
|
this.sourceDOM = sourceDOM
|
||||||
this.mimeType = 'text/css'
|
this.mimeType = 'text/css'
|
||||||
}
|
}
|
||||||
|
|
||||||
load () {
|
async load () {
|
||||||
// get css stylesheets
|
// get css stylesheets
|
||||||
const cssLinks = this.sourceDOM( 'link[rel=stylesheet][media!=print]' ).toArray()
|
const cssLinks = this.sourceDOM( 'link[rel=stylesheet][media!=print]' ).toArray()
|
||||||
const requests = cssLinks.map( elem => this.transformCss( elem.attribs.href ))
|
const requests = cssLinks.map( elem => this.getCss( elem.attribs.href ))
|
||||||
|
|
||||||
const stub = osPath.resolve( module.filename, '../stub.css' )
|
const stub = osPath.resolve( module.filename, '../stub.css' )
|
||||||
requests.unshift( fs.readFile( stub ))
|
requests.unshift( fs.readFile( stub ))
|
||||||
|
|
||||||
return Promise.all( requests )
|
const chunks = await Promise.all( requests )
|
||||||
.then( chunks => chunks.join( '\n' ))
|
return chunks.join( '\n' )
|
||||||
}
|
}
|
||||||
|
|
||||||
async transformCss( cssUrl ) {
|
async getCss( cssUrl ) {
|
||||||
let css = new StyleItem( cssUrl )
|
let css = new Style( cssUrl )
|
||||||
const src = await css.data()
|
const src = await css.getData()
|
||||||
|
|
||||||
// collect urls using dummy replacements
|
|
||||||
const urlre = /(url\(['"]?)([^\)]*[^\)'"])(['"]?\))/g
|
|
||||||
const requests = []
|
|
||||||
src.replace( urlre, ( match, start, url, end ) => {
|
|
||||||
if ( ! url.startsWith( 'data:' )) {
|
|
||||||
const cssItem = new StyleItem( urlconv.resolve( cssUrl, url ))
|
|
||||||
requests.push( cssItem.process() )
|
|
||||||
}
|
|
||||||
return match
|
|
||||||
})
|
|
||||||
const resolvedUrls = await Promise.all( requests )
|
|
||||||
const transformed = src.replace( urlre, ( match, start, url, end ) => {
|
|
||||||
const rurl = resolvedUrls.shift()
|
|
||||||
if ( rurl == null )
|
|
||||||
return match
|
|
||||||
return start + rurl.slice( 3 ) + end
|
|
||||||
})
|
|
||||||
|
|
||||||
const outcss = `/*
|
const outcss = `/*
|
||||||
*
|
*
|
||||||
* from ${cssUrl}
|
* from ${cssUrl}
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
${transformed}
|
${src}
|
||||||
`
|
`
|
||||||
return outcss
|
return outcss
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user