2016-08-23 02:28:30 +12:00
/* global chrome */
'use strict'
2016-06-20 08:28:28 +12:00
2016-06-28 09:19:01 +12:00
import FimFic2Epub from './FimFic2Epub'
2016-08-23 02:28:30 +12:00
import m from 'mithril'
2016-08-15 21:11:20 +12:00
import { saveAs } from 'file-saver'
2016-08-24 19:09:43 +12:00
import autosize from 'autosize'
2016-08-15 21:11:20 +12:00
2016-08-24 08:04:38 +12:00
function blobToDataURL ( blob ) {
return new Promise ( ( resolve , reject ) => {
let fr = new FileReader ( )
fr . onloadend = function ( e ) { resolve ( fr . result ) }
fr . readAsDataURL ( blob )
} )
2016-08-24 02:32:55 +12:00
}
function blobToArrayBuffer ( blob ) {
return new Promise ( ( resolve , reject ) => {
let fr = new FileReader ( )
fr . onloadend = function ( e ) { resolve ( fr . result ) }
fr . readAsArrayBuffer ( blob )
} )
2016-08-15 21:11:20 +12:00
}
2016-06-20 08:28:28 +12:00
2016-08-23 02:28:30 +12:00
const isChromeExt = typeof chrome !== 'undefined'
2016-06-21 09:04:08 +12:00
const STORY _ID = document . location . pathname . match ( /^\/story\/(\d*)/ ) [ 1 ]
2016-06-20 08:28:28 +12:00
2016-08-23 02:28:30 +12:00
let ffc
2016-06-20 08:28:28 +12:00
2016-06-28 09:19:01 +12:00
const epubButton = document . querySelector ( '.story_container ul.chapters li.bottom a[title="Download Story (.epub)"]' )
2016-06-21 09:04:08 +12:00
2016-08-23 02:28:30 +12:00
const dialogContainer = document . createElement ( 'div' )
dialogContainer . id = 'epubDialogContainer'
document . body . appendChild ( dialogContainer )
let checkbox = {
view : function ( ctrl , args , text ) {
2016-08-24 08:04:38 +12:00
return m ( 'label.toggleable-switch' , { style : 'white-space: nowrap;' } , [
m ( 'input' , { type : 'checkbox' , name : args . name , checked : args . checked , onchange : args . onchange } ) ,
m ( 'a' , { style : 'margin-right: 10px' } ) ,
2016-08-23 02:28:30 +12:00
text
] )
}
}
2016-08-25 00:47:48 +12:00
let ffcProgress = m . prop ( 0 )
2016-08-24 02:32:55 +12:00
let ffcStatus = m . prop ( '' )
2016-08-23 02:28:30 +12:00
let dialog = {
controller ( args ) {
2016-08-24 19:09:43 +12:00
const ctrl = this
2016-08-25 00:47:48 +12:00
ffcProgress ( 0 )
2016-08-24 08:04:38 +12:00
this . isLoading = m . prop ( true )
2016-08-23 02:28:30 +12:00
this . dragging = m . prop ( false )
2016-08-23 07:57:19 +12:00
this . xpos = m . prop ( 0 )
this . ypos = m . prop ( 0 )
2016-08-23 02:28:30 +12:00
this . el = m . prop ( null )
2016-08-24 02:32:55 +12:00
this . coverFile = m . prop ( null )
2016-08-24 08:04:38 +12:00
this . coverUrl = m . prop ( '' )
this . checkboxCoverUrl = m . prop ( false )
2016-08-24 09:49:27 +12:00
this . title = m . prop ( '' )
this . author = m . prop ( '' )
2016-08-25 00:47:48 +12:00
this . subjects = m . prop ( [ ] )
2016-08-24 09:49:27 +12:00
this . addCommentsLink = m . prop ( ffc . options . addCommentsLink )
this . includeAuthorNotes = m . prop ( ffc . options . includeAuthorNotes )
this . addChapterHeadings = m . prop ( ffc . options . addChapterHeadings )
2016-08-25 00:47:48 +12:00
this . includeExternal = m . prop ( ffc . options . includeExternal )
this . joinSubjects = m . prop ( ffc . options . joinSubjects )
this . onOpen = function ( el , isInitialized ) {
if ( ! isInitialized ) {
this . el ( el )
this . center ( )
this . isLoading ( true )
ffc . fetchMetadata ( ) . then ( ( ) => {
this . isLoading ( false )
ffcProgress ( - 1 )
this . title ( ffc . storyInfo . title )
this . author ( ffc . storyInfo . author . name )
this . subjects ( ffc . subjects . slice ( 0 ) )
m . redraw ( true )
this . center ( )
ffc . fetchChapters ( ) . then ( ( ) => {
ffcProgress ( - 1 )
m . redraw ( )
} )
} )
}
}
2016-08-24 02:32:55 +12:00
this . setCoverFile = ( e ) => {
this . coverFile ( e . target . files ? e . target . files [ 0 ] : null )
}
2016-08-24 19:09:43 +12:00
this . setSubjects = function ( ) {
// 'this' is the textarea
2016-08-25 00:47:48 +12:00
let set = new Set ( )
ctrl . subjects ( this . value . split ( '\n' ) . map ( ( s ) => s . trim ( ) ) . filter ( ( s ) => {
if ( ! s ) return false
if ( set . has ( s ) ) return false
set . add ( s )
return true
} ) )
2016-08-24 19:09:43 +12:00
this . value = ctrl . subjects ( ) . join ( '\n' )
autosize . update ( this )
}
2016-08-23 02:28:30 +12:00
this . ondown = ( e ) => {
2016-08-23 07:57:19 +12:00
let rect = this . el ( ) . firstChild . getBoundingClientRect ( )
let offset = { x : e . pageX - rect . left - document . body . scrollLeft , y : e . pageY - rect . top - document . body . scrollTop }
2016-08-23 02:28:30 +12:00
this . dragging ( true )
let onmove = ( e ) => {
e . preventDefault ( )
if ( this . dragging ( ) ) {
2016-08-24 09:49:27 +12:00
this . move ( e . pageX - offset . x , e . pageY - offset . y )
2016-08-15 21:11:20 +12:00
}
2016-08-23 02:28:30 +12:00
}
let onup = ( ) => {
this . dragging ( false )
window . removeEventListener ( 'mousemove' , onmove )
window . removeEventListener ( 'mouseup' , onup )
}
window . addEventListener ( 'mousemove' , onmove , false )
window . addEventListener ( 'mouseup' , onup , false )
}
2016-08-25 00:47:48 +12:00
2016-08-24 09:49:27 +12:00
this . move = ( xpos , ypos ) => {
2016-08-25 00:47:48 +12:00
let bc = document . querySelector ( '.body_container' )
let rect = this . el ( ) . firstChild . getBoundingClientRect ( )
this . xpos ( Math . max ( 0 , Math . min ( xpos , bc . offsetWidth - rect . width ) ) )
this . ypos ( Math . max ( 0 , Math . min ( ypos , bc . offsetHeight - rect . height ) ) )
2016-08-23 07:57:19 +12:00
this . el ( ) . style . left = this . xpos ( ) + 'px'
this . el ( ) . style . top = this . ypos ( ) + 'px'
}
2016-08-24 08:04:38 +12:00
this . center = ( ) => {
2016-08-25 00:47:48 +12:00
if ( this . dragging ( ) ) return
2016-08-24 08:04:38 +12:00
let rect = this . el ( ) . firstChild . getBoundingClientRect ( )
2016-08-24 09:49:27 +12:00
this . move (
2016-08-25 00:47:48 +12:00
Math . max ( document . body . scrollLeft , ( window . innerWidth / 2 ) - ( rect . width / 2 ) + document . body . scrollLeft ) ,
Math . max ( document . body . scrollTop , ( window . innerHeight / 2 ) - ( rect . height / 2 ) + document . body . scrollTop )
2016-08-24 09:49:27 +12:00
)
2016-08-24 08:04:38 +12:00
}
2016-08-23 07:57:19 +12:00
this . createEpub = ( e ) => {
2016-08-24 02:32:55 +12:00
ffcProgress ( 0 )
ffcStatus ( '' )
2016-08-23 07:57:19 +12:00
e . target . disabled = true
2016-08-24 02:32:55 +12:00
let chain = Promise . resolve ( )
2016-08-24 08:04:38 +12:00
ffc . coverUrl = ''
ffc . coverImage = null
if ( this . checkboxCoverUrl ( ) ) {
ffc . coverUrl = this . coverUrl ( )
} else if ( this . coverFile ( ) ) {
chain = chain . then ( blobToArrayBuffer . bind ( null , this . coverFile ( ) ) ) . then ( ffc . setCoverImage . bind ( ffc ) )
2016-08-24 02:32:55 +12:00
}
2016-08-24 09:49:27 +12:00
ffc . setTitle ( this . title ( ) )
ffc . setAuthorName ( this . author ( ) )
ffc . options . addCommentsLink = this . addCommentsLink ( )
ffc . options . includeAuthorNotes = this . includeAuthorNotes ( )
ffc . options . addChapterHeadings = this . addChapterHeadings ( )
2016-08-25 00:47:48 +12:00
ffc . options . includeExternal = this . includeExternal ( )
ffc . subjects = this . subjects ( )
ffc . options . joinSubjects = this . joinSubjects ( )
2016-08-24 02:32:55 +12:00
m . redraw ( )
chain
2016-08-25 00:47:48 +12:00
. then ( ffc . fetchAll . bind ( ffc ) )
2016-08-23 07:57:19 +12:00
. then ( ffc . build . bind ( ffc ) )
. then ( ffc . getFile . bind ( ffc ) ) . then ( ( file ) => {
console . log ( 'Saving file...' )
if ( typeof safari !== 'undefined' ) {
2016-08-24 08:04:38 +12:00
blobToDataURL ( file ) . then ( ( dataurl ) => {
2016-08-23 07:57:19 +12:00
document . location . href = dataurl
alert ( 'Add .epub to the filename of the downloaded file' )
} )
} else {
saveAs ( file , ffc . filename )
}
} )
}
2016-08-23 02:28:30 +12:00
} ,
2016-08-24 02:32:55 +12:00
2016-08-23 02:28:30 +12:00
view ( ctrl , args , extras ) {
2016-08-23 07:57:19 +12:00
return m ( '.drop-down-pop-up-container' , { config : ctrl . onOpen . bind ( ctrl ) } , m ( '.drop-down-pop-up' , [
m ( 'h1' , { onmousedown : ctrl . ondown } , m ( 'i.fa.fa-book' ) , 'Export to EPUB' , m ( 'a.close_button' , { onclick : closeDialog } ) ) ,
2016-08-25 00:47:48 +12:00
m ( '.drop-down-pop-up-content' , [
ctrl . isLoading ( ) ? m ( 'div' , { style : 'text-align:center;' } , m ( 'i.fa.fa-spin.fa-spinner' , { style : 'font-size:50px; margin:20px; color:#777;' } ) ) : m ( 'table.properties' , [
2016-08-24 09:49:27 +12:00
m ( 'tr' , m ( 'td.section_header' , { colspan : 3 } , m ( 'b' , 'General settings' ) ) ) ,
m ( 'tr' , m ( 'td.label' , 'Title' ) , m ( 'td' , { colspan : 2 } , m ( 'input' , { type : 'text' , value : ctrl . title ( ) , onchange : m . withAttr ( 'value' , ctrl . title ) } ) ) ) ,
m ( 'tr' , m ( 'td.label' , 'Author' ) , m ( 'td' , { colspan : 2 } , m ( 'input' , { type : 'text' , value : ctrl . author ( ) , onchange : m . withAttr ( 'value' , ctrl . author ) } ) ) ) ,
m ( 'tr' , m ( 'td.label' , 'Custom cover image' ) ,
m ( 'td' ,
ctrl . checkboxCoverUrl ( ) ? m ( 'input' , { type : 'url' , placeholder : 'Image URL' , onchange : m . withAttr ( 'value' , ctrl . coverUrl ) } ) : m ( 'input' , { type : 'file' , accept : 'image/*' , onchange : ctrl . setCoverFile } )
) ,
m ( 'td' , { style : 'width: 1px' } , m ( checkbox , { checked : ctrl . checkboxCoverUrl ( ) , onchange : m . withAttr ( 'checked' , ctrl . checkboxCoverUrl ) } , 'Use image URL' ) )
) ,
m ( 'tr' , m ( 'td.label' , '' ) , m ( 'td' , { colspan : 2 } ,
m ( checkbox , { checked : ctrl . addChapterHeadings ( ) , onchange : m . withAttr ( 'checked' , ctrl . addChapterHeadings ) } , 'Add chapter headings' ) ,
2016-08-25 00:47:48 +12:00
m ( checkbox , { checked : ctrl . addCommentsLink ( ) , onchange : m . withAttr ( 'checked' , ctrl . addCommentsLink ) } , 'Add link to online comments (at the end of chapters)' ) ,
m ( checkbox , { checked : ctrl . includeAuthorNotes ( ) , onchange : m . withAttr ( 'checked' , ctrl . includeAuthorNotes ) } , 'Include author\'s notes' ) ,
m ( checkbox , { checked : ctrl . includeExternal ( ) , onchange : m . withAttr ( 'checked' , ctrl . includeExternal ) } , 'Download & include remote content (embed images)' ) ,
m ( 'div' , { style : 'font-size: 0.9em; line-height: 1em; margin-top: 4px; margin-bottom: 6px; color: #777;' } , 'Note: Disabling this creates invalid EPUBs and requires internet access to see remote content. Only cover image will be embedded.' )
2016-08-24 09:49:27 +12:00
) ) ,
m ( 'tr' , m ( 'td.section_header' , { colspan : 3 } , m ( 'b' , 'Metadata customization' ) ) ) ,
2016-08-25 00:47:48 +12:00
m ( 'tr' , m ( 'td.label' , { style : 'vertical-align: top;' } , 'Categories' ) , m ( 'td' , { colspan : 2 } ,
2016-08-24 19:09:43 +12:00
m ( 'textarea' , { rows : 2 , config : autosize , onchange : ctrl . setSubjects } , ctrl . subjects ( ) . join ( '\n' ) ) ,
2016-08-25 00:47:48 +12:00
m ( checkbox , { checked : ctrl . joinSubjects ( ) , onchange : m . withAttr ( 'checked' , ctrl . joinSubjects ) } , 'Join categories into one, separated by commas' )
2016-08-24 02:32:55 +12:00
) )
2016-08-23 07:57:19 +12:00
] ) ,
m ( '.drop-down-pop-up-footer' , [
2016-08-25 00:47:48 +12:00
m ( 'button.styled_button' , { onclick : ctrl . createEpub , disabled : ffcProgress ( ) >= 0 && ffcProgress ( ) < 1 , style : 'float: right' } , 'Download EPUB' ) ,
ffcProgress ( ) >= 0 ? m ( '.rating_container' ,
2016-08-24 08:04:38 +12:00
m ( '.bars_container' , m ( '.bar_container' , m ( '.bar_dislike' , m ( '.bar.bar_like' , { style : { width : Math . max ( 0 , ffcProgress ( ) ) * 100 + '%' } } ) ) ) ) ,
2016-08-24 02:32:55 +12:00
' ' ,
2016-08-25 00:47:48 +12:00
ffcProgress ( ) >= 0 && ffcProgress ( ) < 1 ? [ m ( 'i.fa.fa-spin.fa-spinner' ) , m . trust ( ' ' ) ] : null ,
2016-08-24 02:32:55 +12:00
ffcStatus ( )
2016-08-25 00:47:48 +12:00
) : null ,
m ( 'div' , { style : 'clear: both' } )
2016-08-23 07:57:19 +12:00
] )
2016-08-23 02:28:30 +12:00
] )
] ) )
}
}
2016-08-24 09:49:27 +12:00
let dialogOpen = false
2016-08-23 02:28:30 +12:00
function openDialog ( args , extras ) {
2016-08-24 09:49:27 +12:00
if ( dialogOpen ) {
return
}
dialogOpen = true
2016-08-23 02:28:30 +12:00
m . mount ( dialogContainer , m ( dialog , args , extras ) )
}
function closeDialog ( ) {
2016-08-24 09:49:27 +12:00
dialogOpen = false
2016-08-23 02:28:30 +12:00
m . mount ( dialogContainer , null )
}
function clickButton ( ) {
if ( ! STORY _ID ) return
2016-08-24 09:49:27 +12:00
if ( ! ffc ) {
ffc = new FimFic2Epub ( STORY _ID )
ffc . on ( 'progress' , ( percent , status ) => {
ffcProgress ( percent )
if ( status ) {
ffcStatus ( status )
}
m . redraw ( )
} )
}
2016-08-23 02:28:30 +12:00
openDialog ( )
}
if ( epubButton ) {
if ( isChromeExt ) {
chrome . runtime . sendMessage ( { showPageAction : true } )
chrome . runtime . onMessage . addListener ( function ( request ) {
if ( request === 'pageAction' ) {
clickButton ( )
}
2016-06-28 19:39:31 +12:00
} )
2016-08-23 02:28:30 +12:00
}
epubButton . addEventListener ( 'click' , function ( e ) {
e . preventDefault ( )
clickButton ( )
2016-06-21 18:39:26 +12:00
} , false )
2016-06-21 09:04:08 +12:00
}