2019-11-02 18:06:16 +00:00
/ *
2020-01-01 17:00:00 +00:00
* Copyright ( C ) 2019 - 2020 Alex Yatskov < alex @ foosoft . net >
2019-11-02 18:06:16 +00:00
* Author : Alex Yatskov < alex @ foosoft . net >
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2020-01-01 17:00:31 +00:00
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
2019-11-02 18:06:16 +00:00
* /
let dictionaryUI = null ;
class SettingsDictionaryListUI {
2019-11-13 01:13:25 +00:00
constructor ( container , template , extraContainer , extraTemplate ) {
2019-11-02 18:06:16 +00:00
this . container = container ;
this . template = template ;
this . extraContainer = extraContainer ;
this . extraTemplate = extraTemplate ;
2019-11-13 01:13:25 +00:00
this . optionsDictionaries = null ;
this . dictionaries = null ;
2019-11-02 18:06:16 +00:00
this . dictionaryEntries = [ ] ;
this . extra = null ;
2019-11-02 20:21:06 +00:00
document . querySelector ( '#dict-delete-confirm' ) . addEventListener ( 'click' , ( e ) => this . onDictionaryConfirmDelete ( e ) , false ) ;
2019-11-02 18:06:16 +00:00
}
2019-11-13 01:13:25 +00:00
setOptionsDictionaries ( optionsDictionaries ) {
this . optionsDictionaries = optionsDictionaries ;
if ( this . dictionaries !== null ) {
this . setDictionaries ( this . dictionaries ) ;
}
}
2019-11-02 18:06:16 +00:00
setDictionaries ( dictionaries ) {
for ( const dictionaryEntry of this . dictionaryEntries ) {
dictionaryEntry . cleanup ( ) ;
}
this . dictionaryEntries = [ ] ;
2019-11-13 01:13:25 +00:00
this . dictionaries = toIterable ( dictionaries ) ;
if ( this . optionsDictionaries === null ) {
return ;
}
2019-11-02 18:06:16 +00:00
let changed = false ;
2019-11-13 01:13:25 +00:00
for ( const dictionaryInfo of this . dictionaries ) {
2019-11-02 18:06:16 +00:00
if ( this . createEntry ( dictionaryInfo ) ) {
changed = true ;
}
}
2019-11-03 14:56:49 +00:00
this . updateDictionaryOrder ( ) ;
2019-11-27 03:01:54 +00:00
const titles = this . dictionaryEntries . map ( ( e ) => e . dictionaryInfo . title ) ;
const removeKeys = Object . keys ( this . optionsDictionaries ) . filter ( ( key ) => titles . indexOf ( key ) < 0 ) ;
2019-11-13 01:13:25 +00:00
if ( removeKeys . length > 0 ) {
2019-11-10 01:49:44 +00:00
for ( const key of toIterable ( removeKeys ) ) {
2019-11-02 18:06:16 +00:00
delete this . optionsDictionaries [ key ] ;
}
changed = true ;
}
if ( changed ) {
this . save ( ) ;
}
}
createEntry ( dictionaryInfo ) {
const title = dictionaryInfo . title ;
let changed = false ;
let optionsDictionary ;
const optionsDictionaries = this . optionsDictionaries ;
2019-11-25 19:19:18 +00:00
if ( hasOwn ( optionsDictionaries , title ) ) {
2019-11-02 18:06:16 +00:00
optionsDictionary = optionsDictionaries [ title ] ;
} else {
optionsDictionary = SettingsDictionaryListUI . createDictionaryOptions ( ) ;
optionsDictionaries [ title ] = optionsDictionary ;
changed = true ;
}
const content = document . importNode ( this . template . content , true ) . firstChild ;
this . dictionaryEntries . push ( new SettingsDictionaryEntryUI ( this , dictionaryInfo , content , optionsDictionary ) ) ;
return changed ;
}
static createDictionaryOptions ( ) {
return utilBackgroundIsolate ( {
priority : 0 ,
enabled : false ,
allowSecondarySearches : false
} ) ;
}
createExtra ( totalCounts , remainders , totalRemainder ) {
const content = document . importNode ( this . extraTemplate . content , true ) . firstChild ;
this . extraContainer . appendChild ( content ) ;
return new SettingsDictionaryExtraUI ( this , totalCounts , remainders , totalRemainder , content ) ;
}
setCounts ( dictionaryCounts , totalCounts ) {
const remainders = Object . assign ( { } , totalCounts ) ;
const keys = Object . keys ( remainders ) ;
for ( let i = 0 , ii = Math . min ( this . dictionaryEntries . length , dictionaryCounts . length ) ; i < ii ; ++ i ) {
const counts = dictionaryCounts [ i ] ;
this . dictionaryEntries [ i ] . setCounts ( counts ) ;
for ( const key of keys ) {
remainders [ key ] -= counts [ key ] ;
}
}
let totalRemainder = 0 ;
for ( const key of keys ) {
totalRemainder += remainders [ key ] ;
}
if ( this . extra !== null ) {
this . extra . cleanup ( ) ;
this . extra = null ;
}
if ( totalRemainder > 0 ) {
this . extra = this . createExtra ( totalCounts , remainders , totalRemainder ) ;
}
}
2019-11-03 14:56:49 +00:00
updateDictionaryOrder ( ) {
const sortInfo = this . dictionaryEntries . map ( ( e , i ) => [ e , i ] ) ;
sortInfo . sort ( ( a , b ) => {
const i = b [ 0 ] . optionsDictionary . priority - a [ 0 ] . optionsDictionary . priority ;
return ( i !== 0 ? i : a [ 1 ] - b [ 1 ] ) ;
} ) ;
for ( const [ e ] of sortInfo ) {
this . container . appendChild ( e . content ) ;
}
}
2019-11-02 18:06:16 +00:00
save ( ) {
// Overwrite
}
2019-11-02 20:21:06 +00:00
onDictionaryConfirmDelete ( e ) {
e . preventDefault ( ) ;
const n = document . querySelector ( '#dict-delete-modal' ) ;
const title = n . dataset . dict ;
delete n . dataset . dict ;
$ ( n ) . modal ( 'hide' ) ;
2019-11-27 03:01:54 +00:00
const index = this . dictionaryEntries . findIndex ( ( e ) => e . dictionaryInfo . title === title ) ;
2019-11-02 20:21:06 +00:00
if ( index >= 0 ) {
this . dictionaryEntries [ index ] . deleteDictionary ( ) ;
}
}
2019-11-02 18:06:16 +00:00
}
class SettingsDictionaryEntryUI {
constructor ( parent , dictionaryInfo , content , optionsDictionary ) {
this . parent = parent ;
this . dictionaryInfo = dictionaryInfo ;
this . optionsDictionary = optionsDictionary ;
this . counts = null ;
this . eventListeners = [ ] ;
2019-11-02 20:21:06 +00:00
this . isDeleting = false ;
2019-11-02 18:06:16 +00:00
this . content = content ;
this . enabledCheckbox = this . content . querySelector ( '.dict-enabled' ) ;
this . allowSecondarySearchesCheckbox = this . content . querySelector ( '.dict-allow-secondary-searches' ) ;
this . priorityInput = this . content . querySelector ( '.dict-priority' ) ;
2019-11-02 20:21:06 +00:00
this . deleteButton = this . content . querySelector ( '.dict-delete-button' ) ;
2019-11-02 18:06:16 +00:00
2019-11-03 17:54:18 +00:00
if ( this . dictionaryInfo . version < 3 ) {
this . content . querySelector ( '.dict-outdated' ) . hidden = false ;
}
2019-11-02 18:06:16 +00:00
this . content . querySelector ( '.dict-title' ) . textContent = this . dictionaryInfo . title ;
this . content . querySelector ( '.dict-revision' ) . textContent = ` rev. ${ this . dictionaryInfo . revision } ` ;
2019-11-24 04:23:08 +00:00
this . content . querySelector ( '.dict-prefix-wildcard-searches-supported' ) . checked = ! ! this . dictionaryInfo . prefixWildcardsSupported ;
2019-11-02 18:06:16 +00:00
this . applyValues ( ) ;
this . addEventListener ( this . enabledCheckbox , 'change' , ( e ) => this . onEnabledChanged ( e ) , false ) ;
this . addEventListener ( this . allowSecondarySearchesCheckbox , 'change' , ( e ) => this . onAllowSecondarySearchesChanged ( e ) , false ) ;
this . addEventListener ( this . priorityInput , 'change' , ( e ) => this . onPriorityChanged ( e ) , false ) ;
2019-11-02 20:21:06 +00:00
this . addEventListener ( this . deleteButton , 'click' , ( e ) => this . onDeleteButtonClicked ( e ) , false ) ;
2019-11-02 18:06:16 +00:00
}
cleanup ( ) {
if ( this . content !== null ) {
if ( this . content . parentNode !== null ) {
this . content . parentNode . removeChild ( this . content ) ;
}
this . content = null ;
}
this . dictionaryInfo = null ;
this . clearEventListeners ( ) ;
}
setCounts ( counts ) {
this . counts = counts ;
const node = this . content . querySelector ( '.dict-counts' ) ;
node . textContent = JSON . stringify ( {
info : this . dictionaryInfo ,
counts
} , null , 4 ) ;
node . removeAttribute ( 'hidden' ) ;
}
save ( ) {
this . parent . save ( ) ;
}
addEventListener ( node , type , listener , options ) {
node . addEventListener ( type , listener , options ) ;
this . eventListeners . push ( [ node , type , listener , options ] ) ;
}
clearEventListeners ( ) {
for ( const [ node , type , listener , options ] of this . eventListeners ) {
node . removeEventListener ( type , listener , options ) ;
}
this . eventListeners = [ ] ;
}
applyValues ( ) {
this . enabledCheckbox . checked = this . optionsDictionary . enabled ;
this . allowSecondarySearchesCheckbox . checked = this . optionsDictionary . allowSecondarySearches ;
this . priorityInput . value = ` ${ this . optionsDictionary . priority } ` ;
}
2019-11-02 20:21:06 +00:00
async deleteDictionary ( ) {
if ( this . isDeleting ) {
return ;
}
const progress = this . content . querySelector ( '.progress' ) ;
progress . hidden = false ;
const progressBar = this . content . querySelector ( '.progress-bar' ) ;
this . isDeleting = true ;
2019-11-02 21:37:53 +00:00
const prevention = new PageExitPrevention ( ) ;
2019-11-02 20:21:06 +00:00
try {
2019-11-02 21:37:53 +00:00
prevention . start ( ) ;
2019-11-02 20:21:06 +00:00
const onProgress = ( { processed , count , storeCount , storesProcesed } ) => {
let percent = 0.0 ;
if ( count > 0 && storesProcesed > 0 ) {
percent = ( processed / count ) * ( storesProcesed / storeCount ) * 100.0 ;
}
progressBar . style . width = ` ${ percent } % ` ;
} ;
await utilDatabaseDeleteDictionary ( this . dictionaryInfo . title , onProgress , { rate : 1000 } ) ;
} catch ( e ) {
dictionaryErrorsShow ( [ e ] ) ;
} finally {
2019-11-02 21:37:53 +00:00
prevention . end ( ) ;
2019-11-02 20:21:06 +00:00
this . isDeleting = false ;
progress . hidden = true ;
2020-02-01 16:40:09 +00:00
onDatabaseUpdated ( ) ;
2019-11-02 20:21:06 +00:00
}
}
2019-11-02 18:06:16 +00:00
onEnabledChanged ( e ) {
this . optionsDictionary . enabled = ! ! e . target . checked ;
this . save ( ) ;
}
onAllowSecondarySearchesChanged ( e ) {
this . optionsDictionary . allowSecondarySearches = ! ! e . target . checked ;
this . save ( ) ;
}
onPriorityChanged ( e ) {
let value = Number . parseFloat ( e . target . value ) ;
if ( Number . isNaN ( value ) ) {
value = this . optionsDictionary . priority ;
} else {
this . optionsDictionary . priority = value ;
this . save ( ) ;
}
e . target . value = ` ${ value } ` ;
2019-11-03 14:56:49 +00:00
this . parent . updateDictionaryOrder ( ) ;
2019-11-02 18:06:16 +00:00
}
2019-11-02 20:21:06 +00:00
onDeleteButtonClicked ( e ) {
e . preventDefault ( ) ;
if ( this . isDeleting ) {
return ;
}
const title = this . dictionaryInfo . title ;
const n = document . querySelector ( '#dict-delete-modal' ) ;
n . dataset . dict = title ;
document . querySelector ( '#dict-remove-modal-dict-name' ) . textContent = title ;
$ ( n ) . modal ( 'show' ) ;
}
2019-11-02 18:06:16 +00:00
}
class SettingsDictionaryExtraUI {
constructor ( parent , totalCounts , remainders , totalRemainder , content ) {
this . parent = parent ;
this . content = content ;
this . content . querySelector ( '.dict-total-count' ) . textContent = ` ${ totalRemainder } item ${ totalRemainder !== 1 ? 's' : '' } ` ;
const node = this . content . querySelector ( '.dict-counts' ) ;
node . textContent = JSON . stringify ( {
counts : totalCounts ,
remainders : remainders
} , null , 4 ) ;
node . removeAttribute ( 'hidden' ) ;
}
cleanup ( ) {
if ( this . content !== null ) {
if ( this . content . parentNode !== null ) {
this . content . parentNode . removeChild ( this . content ) ;
}
this . content = null ;
}
}
}
async function dictSettingsInitialize ( ) {
dictionaryUI = new SettingsDictionaryListUI (
document . querySelector ( '#dict-groups' ) ,
document . querySelector ( '#dict-template' ) ,
document . querySelector ( '#dict-groups-extra' ) ,
2019-11-13 01:13:25 +00:00
document . querySelector ( '#dict-extra-template' )
2019-11-02 18:06:16 +00:00
) ;
2019-11-13 01:01:55 +00:00
dictionaryUI . save = ( ) => settingsSaveOptions ( ) ;
2019-11-02 18:06:16 +00:00
2019-11-02 18:30:17 +00:00
document . querySelector ( '#dict-purge-button' ) . addEventListener ( 'click' , ( e ) => onDictionaryPurgeButtonClick ( e ) , false ) ;
document . querySelector ( '#dict-purge-confirm' ) . addEventListener ( 'click' , ( e ) => onDictionaryPurge ( e ) , false ) ;
2019-11-02 18:06:16 +00:00
document . querySelector ( '#dict-file-button' ) . addEventListener ( 'click' , ( e ) => onDictionaryImportButtonClick ( e ) , false ) ;
document . querySelector ( '#dict-file' ) . addEventListener ( 'change' , ( e ) => onDictionaryImport ( e ) , false ) ;
document . querySelector ( '#dict-main' ) . addEventListener ( 'change' , ( e ) => onDictionaryMainChanged ( e ) , false ) ;
2019-11-24 03:54:06 +00:00
document . querySelector ( '#database-enable-prefix-wildcard-searches' ) . addEventListener ( 'change' , ( e ) => onDatabaseEnablePrefixWildcardSearchesChanged ( e ) , false ) ;
2019-11-02 18:06:16 +00:00
2020-02-01 16:20:50 +00:00
onDictionaryOptionsChanged ( ) ;
2020-02-01 16:40:09 +00:00
onDatabaseUpdated ( ) ;
2019-11-02 18:06:16 +00:00
}
2020-02-01 16:20:50 +00:00
async function onDictionaryOptionsChanged ( ) {
2019-11-13 01:13:25 +00:00
if ( dictionaryUI === null ) { return ; }
2020-02-01 16:20:50 +00:00
const optionsContext = getOptionsContext ( ) ;
const options = await getOptionsMutable ( optionsContext ) ;
2019-11-13 01:13:25 +00:00
dictionaryUI . setOptionsDictionaries ( options . dictionaries ) ;
2019-11-24 03:54:06 +00:00
const optionsFull = await apiOptionsGetFull ( ) ;
document . querySelector ( '#database-enable-prefix-wildcard-searches' ) . checked = optionsFull . global . database . prefixWildcardsSupported ;
2019-11-13 01:13:25 +00:00
}
2020-02-01 16:40:09 +00:00
async function onDatabaseUpdated ( ) {
2019-11-02 18:06:16 +00:00
try {
const dictionaries = await utilDatabaseGetDictionaryInfo ( ) ;
dictionaryUI . setDictionaries ( dictionaries ) ;
2019-11-02 18:39:37 +00:00
document . querySelector ( '#dict-warning' ) . hidden = ( dictionaries . length > 0 ) ;
2020-02-01 16:40:09 +00:00
updateMainDictionarySelectOptions ( dictionaries ) ;
await updateMainDictionarySelectValue ( ) ;
2019-11-02 18:06:16 +00:00
2019-11-27 03:01:54 +00:00
const { counts , total } = await utilDatabaseGetDictionaryCounts ( dictionaries . map ( ( v ) => v . title ) , true ) ;
2019-11-02 18:06:16 +00:00
dictionaryUI . setCounts ( counts , total ) ;
} catch ( e ) {
dictionaryErrorsShow ( [ e ] ) ;
}
}
2020-02-01 16:40:09 +00:00
function updateMainDictionarySelectOptions ( dictionaries ) {
2019-11-02 18:06:16 +00:00
const select = document . querySelector ( '#dict-main' ) ;
select . textContent = '' ; // Empty
let option = document . createElement ( 'option' ) ;
option . className = 'text-muted' ;
option . value = '' ;
option . textContent = 'Not selected' ;
select . appendChild ( option ) ;
for ( const { title , sequenced } of toIterable ( dictionaries ) ) {
if ( ! sequenced ) { continue ; }
option = document . createElement ( 'option' ) ;
option . value = title ;
option . textContent = title ;
select . appendChild ( option ) ;
2020-02-01 16:40:09 +00:00
}
}
async function updateMainDictionarySelectValue ( ) {
const optionsContext = getOptionsContext ( ) ;
const options = await apiOptionsGet ( optionsContext ) ;
const value = options . general . mainDictionary ;
const select = document . querySelector ( '#dict-main' ) ;
let selectValue = null ;
for ( const child of select . children ) {
if ( child . nodeName . toUpperCase ( ) === 'OPTION' && child . value === value ) {
selectValue = value ;
break ;
}
}
2019-11-02 18:06:16 +00:00
2020-02-01 16:40:09 +00:00
let missingNodeOption = select . querySelector ( 'option[data-not-installed=true]' ) ;
if ( selectValue === null ) {
if ( missingNodeOption === null ) {
missingNodeOption = document . createElement ( 'option' ) ;
missingNodeOption . className = 'text-muted' ;
missingNodeOption . value = value ;
missingNodeOption . textContent = ` ${ value } (Not installed) ` ;
missingNodeOption . dataset . notInstalled = 'true' ;
select . appendChild ( missingNodeOption ) ;
}
} else {
if ( missingNodeOption !== null ) {
missingNodeOption . parentNode . removeChild ( missingNodeOption ) ;
2019-11-02 18:06:16 +00:00
}
}
select . value = value ;
}
async function onDictionaryMainChanged ( e ) {
2020-02-01 16:42:27 +00:00
const select = e . target ;
const value = select . value ;
const missingNodeOption = select . querySelector ( 'option[data-not-installed=true]' ) ;
if ( missingNodeOption !== null && missingNodeOption . value !== value ) {
missingNodeOption . parentNode . removeChild ( missingNodeOption ) ;
}
2019-11-02 18:06:16 +00:00
const optionsContext = getOptionsContext ( ) ;
2019-12-12 02:11:07 +00:00
const options = await getOptionsMutable ( optionsContext ) ;
2019-11-02 18:06:16 +00:00
options . general . mainDictionary = value ;
2020-02-01 16:40:17 +00:00
await settingsSaveOptions ( ) ;
2019-11-02 18:06:16 +00:00
}
function dictionaryErrorToString ( error ) {
if ( error . toString ) {
error = error . toString ( ) ;
} else {
error = ` ${ error } ` ;
}
for ( const [ match , subst ] of dictionaryErrorToString . overrides ) {
if ( error . includes ( match ) ) {
error = subst ;
break ;
}
}
return error ;
}
dictionaryErrorToString . overrides = [
[
'A mutation operation was attempted on a database that did not allow mutations.' ,
'Access to IndexedDB appears to be restricted. Firefox seems to require that the history preference is set to "Remember history" before IndexedDB use of any kind is allowed.'
] ,
[
'The operation failed for reasons unrelated to the database itself and not covered by any other error code.' ,
'Unable to access IndexedDB due to a possibly corrupt user profile. Try using the "Refresh Firefox" feature to reset your user profile.'
] ,
[
'BulkError' ,
'Unable to finish importing dictionary data into IndexedDB. This may indicate that you do not have sufficient disk space available to complete this operation.'
]
] ;
function dictionaryErrorsShow ( errors ) {
2019-11-02 18:39:37 +00:00
const dialog = document . querySelector ( '#dict-error' ) ;
dialog . textContent = '' ;
2019-11-02 18:06:16 +00:00
if ( errors !== null && errors . length > 0 ) {
const uniqueErrors = { } ;
for ( let e of errors ) {
console . error ( e ) ;
e = dictionaryErrorToString ( e ) ;
2019-11-25 19:19:18 +00:00
uniqueErrors [ e ] = hasOwn ( uniqueErrors , e ) ? uniqueErrors [ e ] + 1 : 1 ;
2019-11-02 18:06:16 +00:00
}
for ( const e in uniqueErrors ) {
const count = uniqueErrors [ e ] ;
const div = document . createElement ( 'p' ) ;
if ( count > 1 ) {
div . textContent = ` ${ e } ` ;
const em = document . createElement ( 'em' ) ;
em . textContent = ` ( ${ count } ) ` ;
div . appendChild ( em ) ;
} else {
div . textContent = ` ${ e } ` ;
}
2019-11-02 18:39:37 +00:00
dialog . appendChild ( div ) ;
2019-11-02 18:06:16 +00:00
}
2019-11-02 18:39:37 +00:00
dialog . hidden = false ;
2019-11-02 18:06:16 +00:00
} else {
2019-11-02 18:39:37 +00:00
dialog . hidden = true ;
2019-11-02 18:06:16 +00:00
}
}
function dictionarySpinnerShow ( show ) {
const spinner = $ ( '#dict-spinner' ) ;
if ( show ) {
spinner . show ( ) ;
} else {
spinner . hide ( ) ;
}
}
function onDictionaryImportButtonClick ( ) {
const dictFile = document . querySelector ( '#dict-file' ) ;
dictFile . click ( ) ;
}
2019-11-02 18:30:17 +00:00
function onDictionaryPurgeButtonClick ( e ) {
e . preventDefault ( ) ;
$ ( '#dict-purge-modal' ) . modal ( 'show' ) ;
}
2019-11-02 18:06:16 +00:00
async function onDictionaryPurge ( e ) {
e . preventDefault ( ) ;
2019-11-02 18:30:17 +00:00
$ ( '#dict-purge-modal' ) . modal ( 'hide' ) ;
2019-11-02 18:06:16 +00:00
const dictControls = $ ( '#dict-importer, #dict-groups, #dict-groups-extra, #dict-main-group' ) . hide ( ) ;
2019-11-02 18:39:37 +00:00
const dictProgress = document . querySelector ( '#dict-purge' ) ;
dictProgress . hidden = false ;
2019-11-02 18:06:16 +00:00
2019-11-02 21:37:53 +00:00
const prevention = new PageExitPrevention ( ) ;
2019-11-02 18:06:16 +00:00
try {
2019-11-02 21:37:53 +00:00
prevention . start ( ) ;
2019-11-02 18:06:16 +00:00
dictionaryErrorsShow ( null ) ;
dictionarySpinnerShow ( true ) ;
await utilDatabasePurge ( ) ;
2019-12-12 02:11:07 +00:00
for ( const { options } of toIterable ( ( await getOptionsFullMutable ( ) ) . profiles ) ) {
2019-11-02 18:06:16 +00:00
options . dictionaries = utilBackgroundIsolate ( { } ) ;
options . general . mainDictionary = '' ;
}
await settingsSaveOptions ( ) ;
2020-02-01 16:40:09 +00:00
onDatabaseUpdated ( ) ;
2019-11-02 18:06:16 +00:00
} catch ( err ) {
dictionaryErrorsShow ( [ err ] ) ;
} finally {
2019-11-02 21:37:53 +00:00
prevention . end ( ) ;
2019-11-02 18:06:16 +00:00
dictionarySpinnerShow ( false ) ;
dictControls . show ( ) ;
2019-11-02 18:39:37 +00:00
dictProgress . hidden = true ;
2019-11-02 18:06:16 +00:00
if ( storageEstimate . mostRecent !== null ) {
storageUpdateStats ( ) ;
}
}
}
async function onDictionaryImport ( e ) {
2019-12-15 20:51:30 +00:00
const files = [ ... e . target . files ] ;
e . target . value = null ;
2019-11-02 18:06:16 +00:00
const dictFile = $ ( '#dict-file' ) ;
const dictControls = $ ( '#dict-importer' ) . hide ( ) ;
const dictProgress = $ ( '#dict-import-progress' ) . show ( ) ;
2019-11-02 20:58:21 +00:00
const dictImportInfo = document . querySelector ( '#dict-import-info' ) ;
2019-11-02 18:06:16 +00:00
2019-11-02 21:37:53 +00:00
const prevention = new PageExitPrevention ( ) ;
2019-11-02 18:06:16 +00:00
try {
2019-11-02 21:37:53 +00:00
prevention . start ( ) ;
2019-11-02 18:06:16 +00:00
dictionaryErrorsShow ( null ) ;
dictionarySpinnerShow ( true ) ;
2019-11-27 03:01:54 +00:00
const setProgress = ( percent ) => dictProgress . find ( '.progress-bar' ) . css ( 'width' , ` ${ percent } % ` ) ;
2019-11-02 18:06:16 +00:00
const updateProgress = ( total , current ) => {
setProgress ( current / total * 100.0 ) ;
if ( storageEstimate . mostRecent !== null && ! storageUpdateStats . isUpdating ) {
storageUpdateStats ( ) ;
}
} ;
2019-11-24 03:54:06 +00:00
const optionsFull = await apiOptionsGetFull ( ) ;
const importDetails = {
prefixWildcardsSupported : optionsFull . global . database . prefixWildcardsSupported
} ;
2019-11-02 20:58:21 +00:00
for ( let i = 0 , ii = files . length ; i < ii ; ++ i ) {
setProgress ( 0.0 ) ;
if ( ii > 1 ) {
dictImportInfo . hidden = false ;
dictImportInfo . textContent = ` ( ${ i + 1 } of ${ ii } ) ` ;
2019-11-02 18:06:16 +00:00
}
2019-12-08 20:52:34 +00:00
const { result , errors } = await utilDatabaseImport ( files [ i ] , updateProgress , importDetails ) ;
2019-12-12 02:11:07 +00:00
for ( const { options } of toIterable ( ( await getOptionsFullMutable ( ) ) . profiles ) ) {
2019-11-02 20:58:21 +00:00
const dictionaryOptions = SettingsDictionaryListUI . createDictionaryOptions ( ) ;
dictionaryOptions . enabled = true ;
2019-12-08 20:52:34 +00:00
options . dictionaries [ result . title ] = dictionaryOptions ;
if ( result . sequenced && options . general . mainDictionary === '' ) {
options . general . mainDictionary = result . title ;
2019-11-02 20:58:21 +00:00
}
}
2019-11-02 18:06:16 +00:00
2019-11-02 20:58:21 +00:00
await settingsSaveOptions ( ) ;
2019-12-08 20:52:34 +00:00
if ( errors . length > 0 ) {
errors . push ( ... errors ) ;
errors . push ( ` Dictionary may not have been imported properly: ${ errors . length } error ${ errors . length === 1 ? '' : 's' } reported. ` ) ;
dictionaryErrorsShow ( errors ) ;
2019-11-02 20:58:21 +00:00
}
2020-02-01 16:40:09 +00:00
onDatabaseUpdated ( ) ;
2019-11-02 20:58:21 +00:00
}
2019-11-02 18:06:16 +00:00
} catch ( err ) {
dictionaryErrorsShow ( [ err ] ) ;
} finally {
2019-11-02 21:37:53 +00:00
prevention . end ( ) ;
2019-11-02 18:06:16 +00:00
dictionarySpinnerShow ( false ) ;
2019-11-02 20:58:21 +00:00
dictImportInfo . hidden = false ;
dictImportInfo . textContent = '' ;
2019-11-02 18:06:16 +00:00
dictFile . val ( '' ) ;
dictControls . show ( ) ;
dictProgress . hide ( ) ;
}
}
2019-11-24 03:54:06 +00:00
async function onDatabaseEnablePrefixWildcardSearchesChanged ( e ) {
2019-12-12 02:11:07 +00:00
const optionsFull = await getOptionsFullMutable ( ) ;
2019-11-24 03:54:06 +00:00
const v = ! ! e . target . checked ;
if ( optionsFull . global . database . prefixWildcardsSupported === v ) { return ; }
optionsFull . global . database . prefixWildcardsSupported = ! ! e . target . checked ;
await settingsSaveOptions ( ) ;
}