Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9405ce2cc0 | ||
|
|
115277d88a | ||
|
|
6925c11dbd | ||
|
|
984d835892 | ||
|
|
23353a4960 | ||
|
|
955bcda032 | ||
|
|
81ad5d7a2c | ||
|
|
dada7980ec | ||
|
|
511a308646 | ||
|
|
65c8f2cc81 | ||
|
|
238c18bc48 | ||
|
|
873a08fee1 | ||
|
|
7e89228803 | ||
|
|
fc630923a4 | ||
|
|
928f911d03 | ||
|
|
7ffcd3fe1b | ||
|
|
0efbaf643a | ||
|
|
f1bf8e54ae | ||
|
|
b4aa6ab6c8 | ||
|
|
72431f0202 | ||
|
|
04a326c0e4 | ||
|
|
931966f4f3 | ||
|
|
8808cc4779 | ||
|
|
988c959eba | ||
|
|
c0b658d9e6 | ||
|
|
3190246f34 | ||
|
|
d957b4a5f9 | ||
|
|
bef9e5705c | ||
|
|
eb2affa518 | ||
|
|
07b9a3c033 | ||
|
|
3ca14ae06a | ||
|
|
7caa0c2112 | ||
|
|
9c69f5bc01 | ||
|
|
125a2e0f17 | ||
|
|
1b4360b897 | ||
|
|
4775d6e47b | ||
|
|
33fc553c55 | ||
|
|
25cad81c50 | ||
|
|
02a2099c1f | ||
|
|
1cda186b1a | ||
|
|
f10291b6c6 | ||
|
|
26ab5d6765 | ||
|
|
5edeed0747 | ||
|
|
c878ce432f | ||
|
|
81798897c8 | ||
|
|
63840fadbc | ||
|
|
36aa057035 | ||
|
|
30afd2421c | ||
|
|
53a1d90bd8 | ||
|
|
2ecf6beef2 | ||
|
|
85910a20e9 | ||
|
|
66cf7790b3 | ||
|
|
4a9eb7e4bb | ||
|
|
07446696c1 | ||
|
|
a29f9d9a4b | ||
|
|
d49c9baec3 | ||
|
|
8c9d4a12ee | ||
|
|
fce69e4657 | ||
|
|
b387509f88 | ||
|
|
8dc8bdc0d6 | ||
|
|
00caae8363 | ||
|
|
2ead8570a7 | ||
|
|
408315466b | ||
|
|
c651836554 | ||
|
|
03a1e70fce | ||
|
|
ab5a11a24f | ||
|
|
8cd6ed472c | ||
|
|
055181b744 | ||
|
|
e331a3920b | ||
|
|
c62bccb470 | ||
|
|
ea351ea293 | ||
|
|
d806a07c60 | ||
|
|
c0ea096f1f | ||
|
|
011d4a1672 |
@@ -11,7 +11,7 @@
|
|||||||
Открыть выбранную закладку
|
Открыть выбранную закладку
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-input ref="search" v-model="search" class="col" rounded outlined dense bg-color="white" placeholder="Найти">
|
<q-input ref="search" v-model="search" class="col" outlined dense bg-color="white" placeholder="Найти">
|
||||||
<template #append>
|
<template #append>
|
||||||
<q-icon v-if="search !== ''" name="la la-times" class="cursor-pointer" @click="resetSearch" />
|
<q-icon v-if="search !== ''" name="la la-times" class="cursor-pointer" @click="resetSearch" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,19 +5,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #buttons>
|
<template #buttons>
|
||||||
<span class="full-screen-button row justify-center items-center" @mousedown.stop @click="fullScreenToggle">
|
<span class="header-button row justify-center items-center" @mousedown.stop @click="fullScreenToggle">
|
||||||
<q-icon :name="(fullScreenActive ? 'la la-compress-arrows-alt': 'la la-expand-arrows-alt')" size="16px" />
|
<q-icon :name="(fullScreenActive ? 'la la-compress-arrows-alt': 'la la-expand-arrows-alt')" size="16px" />
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">На весь экран</q-tooltip>
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">На весь экран</q-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<span class="full-screen-button row justify-center items-center" @mousedown.stop @click="changeScale(0.1)">
|
<span class="header-button row justify-center items-center" @mousedown.stop @click="changeScale(0.1)">
|
||||||
<q-icon name="la la-plus" size="16px" />
|
<q-icon name="la la-plus" size="16px" />
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Увеличить масштаб</q-tooltip>
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Увеличить масштаб</q-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<span class="full-screen-button row justify-center items-center" @mousedown.stop @click="changeScale(-0.1)">
|
<span class="header-button row justify-center items-center" @mousedown.stop @click="changeScale(-0.1)">
|
||||||
<q-icon name="la la-minus" size="16px" />
|
<q-icon name="la la-minus" size="16px" />
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Уменьшить масштаб</q-tooltip>
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Уменьшить масштаб</q-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<span class="full-screen-button row justify-center items-center" @mousedown.stop @click="showHelp">
|
<span class="header-button row justify-center items-center" @mousedown.stop @click="showHelp">
|
||||||
<q-icon name="la la-question-circle" size="16px" />
|
<q-icon name="la la-question-circle" size="16px" />
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Справка</q-tooltip>
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">Справка</q-tooltip>
|
||||||
</span>
|
</span>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
:options="rootLinkOptions"
|
:options="rootLinkOptions"
|
||||||
style="width: 230px"
|
style="width: 230px"
|
||||||
dropdown-icon="la la-angle-down la-sm"
|
dropdown-icon="la la-angle-down la-sm"
|
||||||
rounded outlined dense emit-value map-options display-value-sanitize options-sanitize
|
outlined dense emit-value map-options display-value-sanitize options-sanitize
|
||||||
@popup-show="onSelectPopupShow" @popup-hide="onSelectPopupHide"
|
@popup-show="onSelectPopupShow" @popup-hide="onSelectPopupHide"
|
||||||
>
|
>
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
:options="selectedLinkOptions"
|
:options="selectedLinkOptions"
|
||||||
style="width: 50px"
|
style="width: 50px"
|
||||||
dropdown-icon="la la-angle-down la-sm"
|
dropdown-icon="la la-angle-down la-sm"
|
||||||
rounded outlined dense emit-value map-options hide-selected display-value-sanitize options-sanitize
|
outlined dense emit-value map-options hide-selected display-value-sanitize options-sanitize
|
||||||
@popup-show="onSelectPopupShow" @popup-hide="onSelectPopupHide"
|
@popup-show="onSelectPopupShow" @popup-hide="onSelectPopupHide"
|
||||||
>
|
>
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
ref="input"
|
ref="input"
|
||||||
v-model="bookUrl"
|
v-model="bookUrl"
|
||||||
class="col q-mr-sm"
|
class="col q-mr-sm"
|
||||||
rounded outlined dense
|
outlined dense
|
||||||
bg-color="white"
|
bg-color="white"
|
||||||
placeholder="Скопируйте сюда ссылку на книгу и нажмите 'Открыть'"
|
placeholder="Скопируйте сюда ссылку на книгу и нажмите 'Открыть'"
|
||||||
@focus="selectAllOnFocus" @keydown="bookUrlKeyDown"
|
@focus="selectAllOnFocus" @keydown="bookUrlKeyDown"
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<q-btn :disabled="!bookUrl" rounded color="green-7" no-caps size="14px" @click="submitUrl">
|
<q-btn :disabled="!bookUrl" color="green-7" no-caps size="14px" @click="submitUrl">
|
||||||
Открыть
|
Открыть
|
||||||
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
||||||
Открыть в читалке
|
Открыть в читалке
|
||||||
@@ -894,14 +894,15 @@ export default vueComponent(ExternalLibs);
|
|||||||
background-color: #A0A0A0;
|
background-color: #A0A0A0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.full-screen-button {
|
.header-button {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.full-screen-button:hover {
|
.header-button:hover {
|
||||||
background-color: #69C05F;
|
color: white;
|
||||||
|
background-color: #39902F;
|
||||||
}
|
}
|
||||||
|
|
||||||
.transparent-layout {
|
.transparent-layout {
|
||||||
|
|||||||
@@ -23,15 +23,15 @@
|
|||||||
|
|
||||||
<div class="q-mb-sm" />
|
<div class="q-mb-sm" />
|
||||||
|
|
||||||
<div v-show="selectedTab == 'contents'" class="tab-panel">
|
<div v-show="selectedTab == 'contents'" ref="tabPanelContents" class="tab-panel">
|
||||||
<div>
|
<div>
|
||||||
<div v-for="item in contents" :key="item.key" class="column" style="width: 540px">
|
<div v-for="item in contents" :key="item.key" class="column" style="width: 540px">
|
||||||
<div class="row q-px-sm no-wrap" :class="{'item': !item.isBookPos, 'item-book-pos': item.isBookPos}">
|
<div :ref="`mainitem${item.key}`" class="row q-px-sm no-wrap" :class="{'item': !item.isBookPos, 'item-book-pos': item.isBookPos}">
|
||||||
<div v-if="item.list.length" class="row justify-center items-center expand-button clickable" @click="expandClick(item.key)">
|
<div v-if="item.list.length" class="row justify-center items-center expand-button clickable" @click="expandClick(item.key)">
|
||||||
<q-icon name="la la-caret-right" class="icon" :class="{'expanded-icon': item.expanded}" color="green-8" size="20px" />
|
<q-icon name="la la-caret-right" class="icon" :class="{'expanded-icon': item.expanded}" color="green-8" size="24px" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="no-expand-button clickable" @click="setBookPos(item.offset)">
|
<div v-else class="no-expand-button clickable" @click="setBookPos(item.offset)">
|
||||||
<q-icon name="la la-stop" class="icon" style="visibility: hidden" size="20px" />
|
<q-icon name="la la-stop" class="icon" style="visibility: hidden" size="24px" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col row clickable" @click="setBookPos(item.offset)">
|
<div class="col row clickable" @click="setBookPos(item.offset)">
|
||||||
<div :style="item.indentStyle"></div>
|
<div :style="item.indentStyle"></div>
|
||||||
@@ -42,8 +42,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="item.expanded" :ref="`subitem${item.key}`" class="subitems-transition">
|
<div v-if="item.expanded" :ref="`subdiv${item.key}`" class="subitems-transition">
|
||||||
<div v-for="subitem in item.list" :key="subitem.key" class="row q-px-sm no-wrap" :class="{'subitem': !subitem.isBookPos, 'subitem-book-pos': subitem.isBookPos}">
|
<div
|
||||||
|
v-for="subitem in item.list"
|
||||||
|
:ref="`subitem${subitem.key}`"
|
||||||
|
:key="subitem.key" class="row q-px-sm no-wrap" :class="{'subitem': !subitem.isBookPos, 'subitem-book-pos': subitem.isBookPos}"
|
||||||
|
>
|
||||||
<div class="col row clickable" @click="setBookPos(subitem.offset)">
|
<div class="col row clickable" @click="setBookPos(subitem.offset)">
|
||||||
<div class="no-expand-button"></div>
|
<div class="no-expand-button"></div>
|
||||||
<div :style="subitem.indentStyle"></div>
|
<div :style="subitem.indentStyle"></div>
|
||||||
@@ -61,10 +65,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-show="selectedTab == 'images'" class="tab-panel">
|
<div v-show="selectedTab == 'images'" ref="tabPanelImages" class="tab-panel">
|
||||||
<div>
|
<div>
|
||||||
<div v-for="item in images" :key="item.key" class="column" style="width: 540px">
|
<div v-for="item in images" :key="item.key" class="column" style="width: 540px">
|
||||||
<div class="row q-px-sm no-wrap" :class="{'item': !item.isBookPos, 'item-book-pos': item.isBookPos}">
|
<div :ref="`image${item.key}`" class="row q-px-sm no-wrap" :class="{'item': !item.isBookPos, 'item-book-pos': item.isBookPos}">
|
||||||
<div class="col row clickable" @click="setBookPos(item.offset)">
|
<div class="col row clickable" @click="setBookPos(item.offset)">
|
||||||
<div class="image-thumb-box row justify-center items-center">
|
<div class="image-thumb-box row justify-center items-center">
|
||||||
<div v-show="!imageLoaded[item.id]" class="image-thumb column justify-center">
|
<div v-show="!imageLoaded[item.id]" class="image-thumb column justify-center">
|
||||||
@@ -124,7 +128,10 @@ const componentOptions = {
|
|||||||
watch: {
|
watch: {
|
||||||
bookPos() {
|
bookPos() {
|
||||||
this.updateBookPosSelection();
|
this.updateBookPosSelection();
|
||||||
}
|
},
|
||||||
|
selectedTab() {
|
||||||
|
this.updateBookPosScrollTop();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
class ContentsPage {
|
class ContentsPage {
|
||||||
@@ -282,31 +289,30 @@ class ContentsPage {
|
|||||||
if (!this.isVisible)
|
if (!this.isVisible)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await utils.sleep(50);
|
await this.$nextTick();
|
||||||
const bp = this.bookPos;
|
const bp = this.bookPos;
|
||||||
|
|
||||||
for (let i = 0; i < this.contents.length; i++) {
|
for (let i = 0; i < this.contents.length; i++) {
|
||||||
const item = this.contents[i];
|
const item = this.contents[i];
|
||||||
const nextOffset = (i < this.contents.length - 1 ? this.contents[i + 1].offset : this.parsed.textLength);
|
const nextOffset = (i < this.contents.length - 1 ? this.contents[i + 1].offset : this.parsed.textLength);
|
||||||
|
|
||||||
|
if (bp >= item.offset && bp < nextOffset) {
|
||||||
|
item.isBookPos = true;
|
||||||
|
} else if (item.isBookPos) {
|
||||||
|
item.isBookPos = false;
|
||||||
|
}
|
||||||
|
|
||||||
for (let j = 0; j < item.list.length; j++) {
|
for (let j = 0; j < item.list.length; j++) {
|
||||||
const subitem = item.list[j];
|
const subitem = item.list[j];
|
||||||
const nextSubOffset = (j < item.list.length - 1 ? item.list[j + 1].offset : nextOffset);
|
const nextSubOffset = (j < item.list.length - 1 ? item.list[j + 1].offset : nextOffset);
|
||||||
|
|
||||||
if (bp >= subitem.offset && bp < nextSubOffset) {
|
if (bp >= subitem.offset && bp < nextSubOffset) {
|
||||||
subitem.isBookPos = true;
|
subitem.isBookPos = true;
|
||||||
this.contents[i] = Object.assign(item, {list: item.list});
|
this.updateBookPosScrollTop('contents', item, subitem, j);
|
||||||
} else if (subitem.isBookPos) {
|
} else if (subitem.isBookPos) {
|
||||||
subitem.isBookPos = false;
|
subitem.isBookPos = false;
|
||||||
this.contents[i] = Object.assign(item, {list: item.list});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bp >= item.offset && bp < nextOffset) {
|
|
||||||
this.contents[i] = Object.assign(item, {isBookPos: true});
|
|
||||||
} else if (item.isBookPos) {
|
|
||||||
this.contents[i] = Object.assign(item, {isBookPos: false});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this.images.length; i++) {
|
for (let i = 0; i < this.images.length; i++) {
|
||||||
@@ -314,11 +320,96 @@ class ContentsPage {
|
|||||||
const nextOffset = (i < this.images.length - 1 ? this.images[i + 1].offset : this.parsed.textLength);
|
const nextOffset = (i < this.images.length - 1 ? this.images[i + 1].offset : this.parsed.textLength);
|
||||||
|
|
||||||
if (bp >= img.offset && bp < nextOffset) {
|
if (bp >= img.offset && bp < nextOffset) {
|
||||||
this.images[i] = Object.assign(img, {isBookPos: true});
|
this.images[i].isBookPos = true;
|
||||||
} else if (img.isBookPos) {
|
} else if (img.isBookPos) {
|
||||||
this.images[i] = Object.assign(img, {isBookPos: false});
|
this.images[i].isBookPos = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.updateBookPosScrollTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*getOffsetTop(key) {
|
||||||
|
let el = this.getFirstElem(this.$refs[`mainitem${key}`]);
|
||||||
|
return (el ? el.offsetTop : 0);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
async updateBookPosScrollTop() {
|
||||||
|
try {
|
||||||
|
await this.$nextTick();
|
||||||
|
|
||||||
|
if (this.selectedTab == 'contents') {
|
||||||
|
let item;
|
||||||
|
let subitem;
|
||||||
|
let i;
|
||||||
|
|
||||||
|
//ищем выделенные item
|
||||||
|
for(const _item of this.contents) {
|
||||||
|
if (_item.isBookPos) {
|
||||||
|
item = _item;
|
||||||
|
for (let ii = 0; ii < item.list.length; ii++) {
|
||||||
|
const _subitem = item.list[ii];
|
||||||
|
if (_subitem.isBookPos) {
|
||||||
|
subitem = _subitem;
|
||||||
|
i = ii;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//вычисляем и смещаем tabPanel.scrollTop
|
||||||
|
let el = this.getFirstElem(this.$refs[`mainitem${item.key}`]);
|
||||||
|
let elShift = 0;
|
||||||
|
if (subitem && item.expanded) {
|
||||||
|
const subEl = this.getFirstElem(this.$refs[`subitem${subitem.key}`]);
|
||||||
|
elShift = el.offsetHeight - subEl.offsetHeight*(i + 1);
|
||||||
|
} else {
|
||||||
|
elShift = el.offsetHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabPanel = this.$refs.tabPanelContents;
|
||||||
|
const halfH = tabPanel.clientHeight/2;
|
||||||
|
const newScrollTop = el.offsetTop - halfH - elShift;
|
||||||
|
if (newScrollTop < 20 + tabPanel.scrollTop - halfH || newScrollTop > -20 + tabPanel.scrollTop + halfH)
|
||||||
|
tabPanel.scrollTop = newScrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.selectedTab == 'images') {
|
||||||
|
let item;
|
||||||
|
|
||||||
|
//ищем выделенные item
|
||||||
|
for(const _item of this.images) {
|
||||||
|
if (_item.isBookPos) {
|
||||||
|
item = _item;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//вычисляем и смещаем tabPanel.scrollTop
|
||||||
|
let el = this.getFirstElem(this.$refs[`image${item.key}`]);
|
||||||
|
|
||||||
|
const tabPanel = this.$refs.tabPanelImages;
|
||||||
|
const halfH = tabPanel.clientHeight/2;
|
||||||
|
const newScrollTop = el.offsetTop - halfH - el.offsetHeight/2;
|
||||||
|
|
||||||
|
if (newScrollTop < 20 + tabPanel.scrollTop - halfH || newScrollTop > -20 + tabPanel.scrollTop + halfH)
|
||||||
|
tabPanel.scrollTop = newScrollTop;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFirstElem(items) {
|
||||||
|
return (Array.isArray(items) ? items[0] : items);
|
||||||
}
|
}
|
||||||
|
|
||||||
async expandClick(key) {
|
async expandClick(key) {
|
||||||
@@ -326,17 +417,17 @@ class ContentsPage {
|
|||||||
const expanded = !item.expanded;
|
const expanded = !item.expanded;
|
||||||
|
|
||||||
if (!expanded) {
|
if (!expanded) {
|
||||||
const subitems = this.$refs[`subitem${key}`];
|
let subdiv = this.getFirstElem(this.$refs[`subdiv${key}`]);
|
||||||
subitems.style.height = '0';
|
subdiv.style.height = '0';
|
||||||
await utils.sleep(200);
|
await utils.sleep(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.contents[key] = Object.assign({}, item, {expanded});
|
this.contents[key].expanded = expanded;
|
||||||
|
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
await this.$nextTick();
|
await this.$nextTick();
|
||||||
const subitems = this.$refs[`subitem${key}`];
|
let subdiv = this.getFirstElem(this.$refs[`subdiv${key}`]);
|
||||||
subitems.style.height = subitems.scrollHeight + 'px';
|
subdiv.style.height = subdiv.scrollHeight + 'px';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,20 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="col column" style="min-width: 600px">
|
<div class="col column" style="min-width: 600px">
|
||||||
<q-btn-toggle
|
<div class="bg-grey-3 row">
|
||||||
v-model="selectedTab"
|
<q-tabs
|
||||||
toggle-color="primary"
|
v-model="selectedTab"
|
||||||
no-caps unelevated
|
active-color="black"
|
||||||
:options="buttons"
|
active-bg-color="white"
|
||||||
/>
|
indicator-color="white"
|
||||||
<div class="separator"></div>
|
dense
|
||||||
|
no-caps
|
||||||
|
inline-label
|
||||||
|
class="bg-grey-4 text-grey-7"
|
||||||
|
>
|
||||||
|
<q-tab v-for="btn in buttons" :key="btn.value" :name="btn.value" :label="btn.label" />
|
||||||
|
</q-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component :is="activePage" ref="page" class="col"></component>
|
<component :is="activePage" ref="page" class="col"></component>
|
||||||
@@ -93,8 +100,4 @@ export default vueComponent(HelpPage);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.separator {
|
|
||||||
height: 1px;
|
|
||||||
background-color: #E0E0E0;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-show="visible" class="column justify-center items-center z-max" style="background-color: rgba(0, 0, 0, 0.8)">
|
<div v-show="visible" class="column justify-center items-center" style="background-color: rgba(0, 0, 0, 0.8); z-index: 100;">
|
||||||
<div class="column justify-start items-center" style="height: 250px">
|
<div class="column justify-start items-center" style="height: 250px">
|
||||||
<q-circular-progress
|
<q-circular-progress
|
||||||
show-value
|
show-value
|
||||||
|
|||||||
@@ -141,6 +141,7 @@
|
|||||||
@load-file="loadFile"
|
@load-file="loadFile"
|
||||||
@book-pos-changed="bookPosChanged"
|
@book-pos-changed="bookPosChanged"
|
||||||
@do-action="doAction"
|
@do-action="doAction"
|
||||||
|
@hide-tool-bar="hideToolBar"
|
||||||
></component>
|
></component>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
|
|
||||||
@@ -201,6 +202,7 @@ import miscApi from '../../api/misc';
|
|||||||
|
|
||||||
import {versionHistory} from './versionHistory';
|
import {versionHistory} from './versionHistory';
|
||||||
import * as utils from '../../share/utils';
|
import * as utils from '../../share/utils';
|
||||||
|
import LockQueue from '../../share/LockQueue';
|
||||||
|
|
||||||
const componentOptions = {
|
const componentOptions = {
|
||||||
components: {
|
components: {
|
||||||
@@ -313,6 +315,8 @@ class Reader {
|
|||||||
this.reader = this.$store.state.reader;
|
this.reader = this.$store.state.reader;
|
||||||
this.config = this.$store.state.config;
|
this.config = this.$store.state.config;
|
||||||
|
|
||||||
|
this.lock = new LockQueue(100);
|
||||||
|
|
||||||
this.$root.addEventHook('key', this.keyHook);
|
this.$root.addEventHook('key', this.keyHook);
|
||||||
|
|
||||||
this.lastActivePage = false;
|
this.lastActivePage = false;
|
||||||
@@ -345,6 +349,13 @@ class Reader {
|
|||||||
this.debouncedSetRecentBook(newValue);
|
this.debouncedSetRecentBook(newValue);
|
||||||
}, 15000, {maxWait: 20000});
|
}, 15000, {maxWait: 20000});
|
||||||
|
|
||||||
|
this.debouncedHideToolBar = _.debounce((event) => {
|
||||||
|
if (this.toolBarHideOnScroll && this.toolBarActive !== !!event.show) {
|
||||||
|
this.commit('reader/setToolBarActive', !!event.show);
|
||||||
|
this.$root.eventHook('resize');
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
|
||||||
document.addEventListener('fullscreenchange', () => {
|
document.addEventListener('fullscreenchange', () => {
|
||||||
this.fullScreenActive = (document.fullscreenElement !== null);
|
this.fullScreenActive = (document.fullscreenElement !== null);
|
||||||
});
|
});
|
||||||
@@ -402,6 +413,7 @@ class Reader {
|
|||||||
this.clickControlActive = this.clickControl;
|
this.clickControlActive = this.clickControl;
|
||||||
this.blinkCachedLoad = settings.blinkCachedLoad;
|
this.blinkCachedLoad = settings.blinkCachedLoad;
|
||||||
this.showToolButton = settings.showToolButton;
|
this.showToolButton = settings.showToolButton;
|
||||||
|
this.toolBarHideOnScroll = settings.toolBarHideOnScroll;
|
||||||
this.enableSitesFilter = settings.enableSitesFilter;
|
this.enableSitesFilter = settings.enableSitesFilter;
|
||||||
this.showNeedUpdateNotify = settings.showNeedUpdateNotify;
|
this.showNeedUpdateNotify = settings.showNeedUpdateNotify;
|
||||||
this.splitToPara = settings.splitToPara;
|
this.splitToPara = settings.splitToPara;
|
||||||
@@ -662,6 +674,10 @@ class Reader {
|
|||||||
this.$root.eventHook('resize');
|
this.$root.eventHook('resize');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hideToolBar(event) {
|
||||||
|
this.debouncedHideToolBar(event);
|
||||||
|
}
|
||||||
|
|
||||||
fullScreenToggle() {
|
fullScreenToggle() {
|
||||||
this.fullScreenActive = !this.fullScreenActive;
|
this.fullScreenActive = !this.fullScreenActive;
|
||||||
if (this.fullScreenActive) {
|
if (this.fullScreenActive) {
|
||||||
@@ -897,7 +913,7 @@ class Reader {
|
|||||||
|
|
||||||
refreshBook() {
|
refreshBook() {
|
||||||
const mrb = this.mostRecentBook();
|
const mrb = this.mostRecentBook();
|
||||||
this.loadBook({url: mrb.url, uploadFileName: mrb.uploadFileName, force: true});
|
this.loadBook(Object.assign({}, mrb, {force: true}));
|
||||||
}
|
}
|
||||||
|
|
||||||
undoAction() {
|
undoAction() {
|
||||||
@@ -1051,7 +1067,7 @@ class Reader {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadBook(opts) {
|
async _loadBook(opts) {
|
||||||
if (!opts || !opts.url) {
|
if (!opts || !opts.url) {
|
||||||
this.mostRecentBook();
|
this.mostRecentBook();
|
||||||
return;
|
return;
|
||||||
@@ -1061,10 +1077,6 @@ class Reader {
|
|||||||
|
|
||||||
let url = encodeURI(decodeURI(opts.url));
|
let url = encodeURI(decodeURI(opts.url));
|
||||||
|
|
||||||
//TODO: убрать конвертирование 'file://' после 06.2021
|
|
||||||
if (url.length == 71 && url.indexOf('file://') == 0)
|
|
||||||
url = url.replace(/^file/, 'disk');
|
|
||||||
|
|
||||||
if ((url.indexOf('http://') != 0) && (url.indexOf('https://') != 0) &&
|
if ((url.indexOf('http://') != 0) && (url.indexOf('https://') != 0) &&
|
||||||
(url.indexOf('disk://') != 0))
|
(url.indexOf('disk://') != 0))
|
||||||
url = 'http://' + url;
|
url = 'http://' + url;
|
||||||
@@ -1091,33 +1103,36 @@ class Reader {
|
|||||||
progress.show();
|
progress.show();
|
||||||
progress.setState({state: 'parse'});
|
progress.setState({state: 'parse'});
|
||||||
|
|
||||||
// есть ли среди недавних
|
// есть ли среди загруженных
|
||||||
const key = bookManager.keyFromUrl(url);
|
let wasOpened = bookManager.findRecentByUrlAndPath(url, opts.path);
|
||||||
let wasOpened = await bookManager.getRecentBook({key});
|
wasOpened = (wasOpened ? _.cloneDeep(wasOpened) : {});
|
||||||
wasOpened = (wasOpened ? wasOpened : {});
|
|
||||||
const bookPos = (opts.bookPos !== undefined ? opts.bookPos : wasOpened.bookPos);
|
wasOpened = Object.assign(wasOpened, {
|
||||||
const bookPosSeen = (opts.bookPos !== undefined ? opts.bookPos : wasOpened.bookPosSeen);
|
path: (opts.path !== undefined ? opts.path : wasOpened.path),
|
||||||
const uploadFileName = (opts.uploadFileName ? opts.uploadFileName : '');
|
bookPos: (opts.bookPos !== undefined ? opts.bookPos : wasOpened.bookPos),
|
||||||
|
bookPosSeen: (opts.bookPos !== undefined ? opts.bookPos : wasOpened.bookPosSeen),
|
||||||
|
uploadFileName: (opts.uploadFileName ? opts.uploadFileName : wasOpened.uploadFileName),
|
||||||
|
});
|
||||||
|
|
||||||
let book = null;
|
let book = null;
|
||||||
|
|
||||||
if (!opts.force) {
|
if (!opts.force) {
|
||||||
// пытаемся загрузить и распарсить книгу в менеджере из локального кэша
|
// пытаемся загрузить и распарсить книгу в менеджере из локального кэша
|
||||||
const bookParsed = await bookManager.getBook({url, path: opts.path}, (prog) => {
|
const bookParsed = await bookManager.getBook(wasOpened, (prog) => {
|
||||||
progress.setState({progress: prog});
|
progress.setState({progress: prog});
|
||||||
});
|
});
|
||||||
|
|
||||||
// если есть в локальном кэше
|
// если есть в локальном кэше
|
||||||
if (bookParsed) {
|
if (bookParsed) {
|
||||||
await bookManager.setRecentBook(Object.assign({bookPos, bookPosSeen}, bookParsed));
|
await bookManager.setRecentBook(Object.assign(wasOpened, bookParsed));
|
||||||
this.mostRecentBook();
|
this.mostRecentBook();
|
||||||
this.addAction(bookPos);
|
this.addAction(wasOpened.bookPos);
|
||||||
this.loaderActive = false;
|
this.loaderActive = false;
|
||||||
progress.hide(); this.progressActive = false;
|
progress.hide(); this.progressActive = false;
|
||||||
this.blinkCachedLoadMessage();
|
this.blinkCachedLoadMessage();
|
||||||
|
|
||||||
this.checkBookPosPercent();
|
this.checkBookPosPercent();
|
||||||
await this.activateClickMapPage();
|
this.activateClickMapPage();//no await
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1131,7 +1146,7 @@ class Reader {
|
|||||||
});
|
});
|
||||||
book = Object.assign({}, wasOpened, {data: resp.data});
|
book = Object.assign({}, wasOpened, {data: resp.data});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//молчим
|
this.$root.notify.error('Конвертированный файл не найден на сервере.<br>Пробуем загрузить оригинал.', 'Ошибка загрузки');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1142,7 +1157,7 @@ class Reader {
|
|||||||
if (!book) {
|
if (!book) {
|
||||||
book = await readerApi.loadBook({
|
book = await readerApi.loadBook({
|
||||||
url,
|
url,
|
||||||
uploadFileName,
|
uploadFileName: wasOpened.uploadFileName,
|
||||||
enableSitesFilter: this.enableSitesFilter,
|
enableSitesFilter: this.enableSitesFilter,
|
||||||
skipHtmlCheck: (this.splitToPara ? true : false),
|
skipHtmlCheck: (this.splitToPara ? true : false),
|
||||||
isText: (this.splitToPara ? true : false),
|
isText: (this.splitToPara ? true : false),
|
||||||
@@ -1159,14 +1174,44 @@ class Reader {
|
|||||||
|
|
||||||
// добавляем в bookManager
|
// добавляем в bookManager
|
||||||
progress.setState({state: 'parse', step: 5});
|
progress.setState({state: 'parse', step: 5});
|
||||||
|
|
||||||
const addedBook = await bookManager.addBook(book, (prog) => {
|
const addedBook = await bookManager.addBook(book, (prog) => {
|
||||||
progress.setState({progress: prog});
|
progress.setState({progress: prog});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// sameBookKey
|
||||||
|
if (url.indexOf('disk://') == 0) {
|
||||||
|
//ищем такой файл в загруженных
|
||||||
|
let found = bookManager.findRecentBySameBookKey(wasOpened.uploadFileName);
|
||||||
|
found = (found ? _.cloneDeep(found) : found);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
if (wasOpened.sameBookKey != found.sameBookKey) {
|
||||||
|
//спрашиваем, надо ли объединить файлы
|
||||||
|
const askResult = bookManager.keysEqual(found.path, addedBook.path) ||
|
||||||
|
await this.$root.stdDialog.askYesNo(`
|
||||||
|
Файл с именем "${wasOpened.uploadFileName}" уже есть в загруженных.
|
||||||
|
<br>Объединить позицию?`, 'Найдена похожая книга');
|
||||||
|
if (askResult) {
|
||||||
|
wasOpened.bookPos = found.bookPos;
|
||||||
|
wasOpened.bookPosSeen = found.bookPosSeen;
|
||||||
|
wasOpened.sameBookKey = found.sameBookKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wasOpened.sameBookKey = wasOpened.uploadFileName;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wasOpened.sameBookKey = addedBook.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bookManager.keysEqual(wasOpened.path, addedBook.path))
|
||||||
|
delete wasOpened.loadTime;
|
||||||
|
|
||||||
// добавляем в историю
|
// добавляем в историю
|
||||||
await bookManager.setRecentBook(Object.assign({bookPos, bookPosSeen, uploadFileName}, addedBook));
|
await bookManager.setRecentBook(Object.assign(wasOpened, addedBook));
|
||||||
this.mostRecentBook();
|
this.mostRecentBook();
|
||||||
this.addAction(bookPos);
|
this.addAction(wasOpened.bookPos);
|
||||||
this.updateRoute(true);
|
this.updateRoute(true);
|
||||||
|
|
||||||
this.loaderActive = false;
|
this.loaderActive = false;
|
||||||
@@ -1177,11 +1222,11 @@ class Reader {
|
|||||||
this.stopBlink = true;
|
this.stopBlink = true;
|
||||||
|
|
||||||
this.checkBookPosPercent();
|
this.checkBookPosPercent();
|
||||||
await this.activateClickMapPage();
|
this.activateClickMapPage();//no await
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
progress.hide(); this.progressActive = false;
|
progress.hide(); this.progressActive = false;
|
||||||
this.loaderActive = true;
|
this.loaderActive = true;
|
||||||
if (!this.showHelpOnErrorIfNeeded(e.message)) {
|
if (!this.showHelpOnErrorIfNeeded(url)) {
|
||||||
this.$root.stdDialog.alert(e.message, 'Ошибка', {color: 'negative'});
|
this.$root.stdDialog.alert(e.message, 'Ошибка', {color: 'negative'});
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1189,7 +1234,16 @@ class Reader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadFile(opts) {
|
async loadBook(opts) {
|
||||||
|
await this.lock.get();
|
||||||
|
try {
|
||||||
|
await this._loadBook(opts);
|
||||||
|
} finally {
|
||||||
|
this.lock.ret();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _loadFile(opts) {
|
||||||
this.progressActive = true;
|
this.progressActive = true;
|
||||||
|
|
||||||
await this.$nextTick();
|
await this.$nextTick();
|
||||||
@@ -1205,7 +1259,7 @@ class Reader {
|
|||||||
|
|
||||||
progress.hide(); this.progressActive = false;
|
progress.hide(); this.progressActive = false;
|
||||||
|
|
||||||
await this.loadBook({url, uploadFileName: opts.file.name, force: true});
|
await this._loadBook({url, uploadFileName: opts.file.name, force: true});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
progress.hide(); this.progressActive = false;
|
progress.hide(); this.progressActive = false;
|
||||||
this.loaderActive = true;
|
this.loaderActive = true;
|
||||||
@@ -1213,6 +1267,15 @@ class Reader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadFile(opts) {
|
||||||
|
await this.lock.get();
|
||||||
|
try {
|
||||||
|
await this._loadFile(opts);
|
||||||
|
} finally {
|
||||||
|
this.lock.ret();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
blinkCachedLoadMessage() {
|
blinkCachedLoadMessage() {
|
||||||
if (!this.blinkCachedLoad)
|
if (!this.blinkCachedLoad)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
<div class="row justify-center">
|
<div class="row justify-center">
|
||||||
<!--q-btn class="q-px-sm" color="primary" dense no-caps rounded @click="openDonate">
|
<!--q-btn class="q-px-sm" color="primary" dense no-caps @click="openDonate">
|
||||||
Помочь проекту
|
Помочь проекту
|
||||||
</q-btn-->
|
</q-btn-->
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,88 +9,157 @@
|
|||||||
|
|
||||||
<a ref="download" style="display: none;" target="_blank"></a>
|
<a ref="download" style="display: none;" target="_blank"></a>
|
||||||
|
|
||||||
<q-table
|
<div id="vs-container" ref="vsContainer" class="recent-books-scroll col">
|
||||||
class="recent-books-table col"
|
<div ref="header" class="scroll-header row bg-blue-2">
|
||||||
:rows="tableData"
|
<q-btn class="tool-button" round @click="showSameBookClick">
|
||||||
row-key="key"
|
<q-icon name="la la-caret-right" class="icon" :class="{'expanded-icon': showSameBook}" color="green-8" size="24px" />
|
||||||
:columns="columns"
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
||||||
:pagination="pagination"
|
Показать/скрыть версии книг
|
||||||
separator="cell"
|
</q-tooltip>
|
||||||
hide-bottom
|
</q-btn>
|
||||||
virtual-scroll
|
|
||||||
dense
|
|
||||||
>
|
|
||||||
<template #header="props">
|
|
||||||
<q-tr :props="props">
|
|
||||||
<q-th key="num" class="td-mp" style="width: 25px" :props="props">
|
|
||||||
<span v-html="props.cols[0].label"></span>
|
|
||||||
</q-th>
|
|
||||||
<q-th key="date" class="td-mp break-word" style="width: 77px" :props="props">
|
|
||||||
<span v-html="props.cols[1].label"></span>
|
|
||||||
</q-th>
|
|
||||||
<q-th key="desc" class="td-mp" style="width: 332px" :props="props" colspan="4">
|
|
||||||
<q-input ref="input" v-model="search"
|
|
||||||
outlined dense rounded style="position: absolute; top: 6px; left: 90px; width: 380px" bg-color="white"
|
|
||||||
placeholder="Найти"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
<template #append>
|
|
||||||
<q-icon v-if="search !== ''" name="la la-times" class="cursor-pointer" @click.stop="resetSearch" />
|
|
||||||
</template>
|
|
||||||
</q-input>
|
|
||||||
<span v-html="props.cols[2].label"></span>
|
|
||||||
</q-th>
|
|
||||||
</q-tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #body="props">
|
<q-btn class="tool-button" round @click="scrollToBegin">
|
||||||
<q-tr :props="props">
|
<q-icon name="la la-arrow-up" color="green-8" size="24px" />
|
||||||
<q-td key="num" :props="props" class="td-mp" auto-width>
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
||||||
<div class="break-word" style="width: 25px">
|
В начало списка
|
||||||
{{ props.row.num }}
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<q-btn class="tool-button" round @click="scrollToEnd">
|
||||||
|
<q-icon name="la la-arrow-down" color="green-8" size="24px" />
|
||||||
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
||||||
|
В конец списка
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<q-btn class="tool-button" round @click="scrollToActiveBook">
|
||||||
|
<q-icon name="la la-location-arrow" color="green-8" size="24px" />
|
||||||
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
||||||
|
На текущую книгу
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
ref="input"
|
||||||
|
v-model="search"
|
||||||
|
class="q-ml-sm q-mt-xs"
|
||||||
|
outlined dense
|
||||||
|
style="width: 185px"
|
||||||
|
bg-color="white"
|
||||||
|
placeholder="Найти"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<q-icon v-if="search !== ''" name="la la-times" class="cursor-pointer" @click.stop="resetSearch" />
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<q-select
|
||||||
|
ref="sortMethod"
|
||||||
|
v-model="sortMethod"
|
||||||
|
class="q-ml-sm q-mt-xs"
|
||||||
|
:options="sortMethodOptions"
|
||||||
|
style="width: 180px"
|
||||||
|
bg-color="white"
|
||||||
|
dropdown-icon="la la-angle-down la-sm"
|
||||||
|
outlined dense emit-value map-options display-value-sanitize options-sanitize
|
||||||
|
options-html display-value-html
|
||||||
|
|
||||||
|
@update:model-value="sortMethodSelected"
|
||||||
|
>
|
||||||
|
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
|
||||||
|
Метод сортировки
|
||||||
|
</q-tooltip>
|
||||||
|
|
||||||
|
<template #selected-item="scope">
|
||||||
|
<div style="height: 28px; padding-top: 2px; overflow: hidden" v-html="scope.opt.label" />
|
||||||
|
</template>
|
||||||
|
</q-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-virtual-scroll
|
||||||
|
ref="virtualScroll"
|
||||||
|
v-slot="{ item, index }"
|
||||||
|
:items="tableData"
|
||||||
|
scroll-target="#vs-container"
|
||||||
|
virtual-scroll-item-size="80"
|
||||||
|
@virtual-scroll="onScroll"
|
||||||
|
>
|
||||||
|
<div class="table-row row" :class="{even: index % 2 > 0, 'active-book': item.active, 'active-parent-book': item.activeParent}">
|
||||||
|
<div v-show="item.inGroup" class="row-part column justify-center items-center" style="width: 40px">
|
||||||
|
<q-icon name="la la-code-branch" size="24px" style="color: green" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row-part column justify-center items-stretch" style="width: 80px">
|
||||||
|
<div class="col row justify-center items-center clickable" @click="loadBook(item)">
|
||||||
|
<q-icon name="la la-book" size="40px" style="color: #dddddd" />
|
||||||
</div>
|
</div>
|
||||||
</q-td>
|
|
||||||
|
|
||||||
<q-td key="date" auto-width :props="props" class="td-mp clickable" @click="loadBook(props.row.url)">
|
<div v-show="!showSameBook && item.group && item.group.length > 0" class="row justify-center" style="font-size: 70%">
|
||||||
<div class="break-word" style="width: 68px">
|
{{ (item.group ? item.group.length + 1 : 0) }} верси{{ wordEnding((item.group ? item.group.length + 1 : 0), 1) }}
|
||||||
{{ props.row.touchDate }}<br>
|
|
||||||
{{ props.row.touchTime }}
|
|
||||||
</div>
|
</div>
|
||||||
</q-td>
|
</div>
|
||||||
|
|
||||||
<q-td key="desc" auto-width :props="props" class="td-mp clickable" @click="loadBook(props.row.url)">
|
<div class="row-part column items-stretch clickable break-word" :style="{ 'width': (350 - 40*(+item.inGroup)) + 'px' }" style="font-size: 75%" @click="loadBook(item)">
|
||||||
<div class="break-word" style="width: 332px; font-size: 90%">
|
<div class="col" style="border: 1px solid #cccccc; border-bottom: 0; padding: 4px" :style="{ 'width': (340 - 40*(+item.inGroup)) + 'px' }">
|
||||||
<div style="color: green">
|
<div class="text-green-10" style="font-size: 105%">
|
||||||
{{ props.row.desc.author }}
|
{{ item.desc.author }}
|
||||||
</div>
|
</div>
|
||||||
<div>{{ props.row.desc.title }}</div>
|
<div>{{ item.desc.title }}</div>
|
||||||
<div class="read-bar" :style="`width: ${332*props.row.readPart}px`"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</q-td>
|
|
||||||
|
|
||||||
<q-td key="links" :props="props" class="td-mp" auto-width>
|
<div class="row" style="font-size: 10px">
|
||||||
<div class="break-word" style="width: 75px; font-size: 90%">
|
<div class="row justify-center items-center row-info-top" style="width: 60px">
|
||||||
<a v-show="isUrl(props.row.url)" :href="props.row.url" target="_blank">Оригинал</a><br>
|
{{ item.desc.textLen }}
|
||||||
<a :href="props.row.path" @click.prevent="downloadBook(props.row.path, props.row.fullTitle)">Скачать FB2</a>
|
</div>
|
||||||
</div>
|
|
||||||
</q-td>
|
|
||||||
|
|
||||||
<q-td key="close" :props="props" class="td-mp" auto-width>
|
<div class="row items-center row-info-top" :style="`width: ${(220 - 40*(+item.inGroup))}px; padding: 1px`">
|
||||||
<div style="width: 38px">
|
<div class="read-bar" :style="`width: ${100*item.readPart}%`"></div>
|
||||||
<q-btn
|
</div>
|
||||||
dense
|
|
||||||
style="width: 30px; height: 30px; padding: 7px 0 7px 0; margin-left: 4px"
|
<div class="row justify-center items-center row-info-top" style="width: 59px">
|
||||||
@click="handleDel(props.row.key)"
|
{{ item.desc.perc }}
|
||||||
>
|
</div>
|
||||||
<q-icon class="la la-times" size="14px" />
|
<div class="row-info-top" style="width: 1px">
|
||||||
</q-btn>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</q-td>
|
|
||||||
<q-td key="last" :props="props" class="no-mp">
|
<div class="row" style="font-size: 10px" :style="{ 'width': (340 - 40*(+item.inGroup)) + 'px' }">
|
||||||
</q-td>
|
<div class="row justify-center items-center row-info-bottom" style="width: 30px">
|
||||||
</q-tr>
|
{{ item.num }}
|
||||||
</template>
|
</div>
|
||||||
</q-table>
|
<div class="col row">
|
||||||
|
<div class="row justify-center items-center row-info-bottom time-info" style="width: 50%">
|
||||||
|
Загружен: {{ item.loadTime }}
|
||||||
|
</div>
|
||||||
|
<div class="row justify-center items-center row-info-bottom time-info" style="width: 50%">
|
||||||
|
Читался: {{ item.touchTime }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row-info-bottom" style="width: 1px">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row-part column justify-center" style="width: 80px; font-size: 75%">
|
||||||
|
<div>
|
||||||
|
<a v-show="isUrl(item.url)" :href="item.url" target="_blank">Оригинал</a><br><br>
|
||||||
|
<a :href="item.path" @click.prevent="downloadBook(item.path, item.fullTitle)">Скачать FB2</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row-part column justify-center">
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
style="width: 30px; height: 30px; padding: 7px 0 7px 0; margin-left: 4px"
|
||||||
|
@click="handleDel(item.key)"
|
||||||
|
>
|
||||||
|
<q-icon class="la la-times" size="14px" />
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-virtual-scroll>
|
||||||
|
</div>
|
||||||
</Window>
|
</Window>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -99,9 +168,10 @@
|
|||||||
import vueComponent from '../../vueComponent.js';
|
import vueComponent from '../../vueComponent.js';
|
||||||
|
|
||||||
import path from 'path-browserify';
|
import path from 'path-browserify';
|
||||||
//import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import * as utils from '../../../share/utils';
|
import * as utils from '../../../share/utils';
|
||||||
|
import LockQueue from '../../../share/LockQueue';
|
||||||
import Window from '../../share/Window.vue';
|
import Window from '../../share/Window.vue';
|
||||||
import bookManager from '../share/bookManager';
|
import bookManager from '../share/bookManager';
|
||||||
import readerApi from '../../../api/reader';
|
import readerApi from '../../../api/reader';
|
||||||
@@ -111,9 +181,15 @@ const componentOptions = {
|
|||||||
Window,
|
Window,
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
search: function() {
|
search() {
|
||||||
this.updateTableData();
|
this.updateTableData();
|
||||||
}
|
},
|
||||||
|
sortMethod() {
|
||||||
|
this.updateTableData();
|
||||||
|
},
|
||||||
|
settings() {
|
||||||
|
this.loadSettings();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
class RecentBooksPage {
|
class RecentBooksPage {
|
||||||
@@ -122,52 +198,18 @@ class RecentBooksPage {
|
|||||||
loading = false;
|
loading = false;
|
||||||
search = '';
|
search = '';
|
||||||
tableData = [];
|
tableData = [];
|
||||||
columns = [];
|
sortMethod = '';
|
||||||
pagination = {};
|
showSameBook = false;
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.firstInit = true;
|
this.commit = this.$store.commit;
|
||||||
this.pagination = {rowsPerPage: 0};
|
|
||||||
|
|
||||||
this.columns = [
|
this.lastScrollTop1 = 0;
|
||||||
{
|
this.lastScrollTop2 = 0;
|
||||||
name: 'num',
|
|
||||||
label: '#',
|
this.lock = new LockQueue(100);
|
||||||
align: 'center',
|
|
||||||
sortable: true,
|
this.loadSettings();
|
||||||
field: 'num',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'date',
|
|
||||||
label: 'Время<br>просм.',
|
|
||||||
align: 'left',
|
|
||||||
field: 'touchDateTime',
|
|
||||||
sortable: true,
|
|
||||||
sort: (a, b, rowA, rowB) => rowA.touchDateTime - rowB.touchDateTime,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'desc',
|
|
||||||
label: 'Название',
|
|
||||||
align: 'left',
|
|
||||||
field: 'descString',
|
|
||||||
sortable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'links',
|
|
||||||
label: '',
|
|
||||||
align: 'left',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'close',
|
|
||||||
label: '',
|
|
||||||
align: 'left',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'last',
|
|
||||||
label: '',
|
|
||||||
align: 'left',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@@ -176,89 +218,185 @@ class RecentBooksPage {
|
|||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
//this.$refs.input.focus();//плохо на планшетах
|
//this.$refs.input.focus();//плохо на планшетах
|
||||||
});
|
});
|
||||||
(async() => {//подгрузка списка
|
|
||||||
if (this.initing)
|
|
||||||
return;
|
|
||||||
this.initing = true;
|
|
||||||
|
|
||||||
if (this.firstInit) {//для отзывчивости
|
this.inited = true;
|
||||||
await this.updateTableData(20);
|
|
||||||
this.firstInit = false;
|
(async() => {
|
||||||
}
|
this.showBar();
|
||||||
await utils.sleep(50);
|
|
||||||
await this.updateTableData();
|
await this.updateTableData();
|
||||||
|
await this.scrollToActiveBook();
|
||||||
this.initing = false;
|
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateTableData(limit) {
|
loadSettings() {
|
||||||
while (this.updating) await utils.sleep(100);
|
const settings = this.settings;
|
||||||
this.updating = true;
|
this.showSameBook = settings.recentShowSameBook;
|
||||||
let result = [];
|
this.sortMethod = settings.recentSortMethod || 'loadTimeDesc';
|
||||||
|
}
|
||||||
|
|
||||||
this.loading = !!limit;
|
get settings() {
|
||||||
const sorted = bookManager.getSortedRecent();
|
return this.$store.state.reader.settings;
|
||||||
|
}
|
||||||
|
|
||||||
let num = 0;
|
async updateTableData() {
|
||||||
for (let i = 0; i < sorted.length; i++) {
|
if (!this.inited)
|
||||||
const book = sorted[i];
|
return;
|
||||||
if (book.deleted)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
num++;
|
await this.lock.get();
|
||||||
if (limit && result.length >= limit)
|
try {
|
||||||
break;
|
let result = [];
|
||||||
|
|
||||||
let d = new Date();
|
const sorted = bookManager.getSortedRecent();
|
||||||
d.setTime(book.touchTime);
|
const activeBook = bookManager.mostRecentBook();
|
||||||
const t = utils.formatDate(d).split(' ');
|
|
||||||
|
|
||||||
let readPart = 0;
|
//подготовка полей
|
||||||
let perc = '';
|
for (const book of sorted) {
|
||||||
let textLen = '';
|
if (book.deleted)
|
||||||
const p = (book.bookPosSeen ? book.bookPosSeen : (book.bookPos ? book.bookPos : 0));
|
continue;
|
||||||
if (book.textLength) {
|
|
||||||
readPart = p/book.textLength;
|
let d = new Date();
|
||||||
perc = ` [${(readPart*100).toFixed(2)}%]`;
|
d.setTime(book.touchTime);
|
||||||
textLen = ` ${Math.round(book.textLength/1000)}k`;
|
const touchTime = utils.formatDate(d);
|
||||||
|
const loadTimeRaw = (book.loadTime ? book.loadTime : 0);//book.addTime);
|
||||||
|
d.setTime(loadTimeRaw);
|
||||||
|
const loadTime = utils.formatDate(d);
|
||||||
|
|
||||||
|
let readPart = 0;
|
||||||
|
let perc = '';
|
||||||
|
let textLen = '';
|
||||||
|
const p = (book.bookPosSeen ? book.bookPosSeen : (book.bookPos ? book.bookPos : 0));
|
||||||
|
if (book.textLength) {
|
||||||
|
readPart = p/book.textLength;
|
||||||
|
perc = `${(readPart*100).toFixed(2)}%`;
|
||||||
|
textLen = `${Math.floor(readPart*book.textLength/1000)}/${Math.floor(book.textLength/1000)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bt = utils.getBookTitle(book.fb2);
|
||||||
|
|
||||||
|
let title = bt.bookTitle;
|
||||||
|
title = (title ? `"${title}"`: '');
|
||||||
|
const author = (bt.author ? bt.author : (bt.bookTitle ? bt.bookTitle : (book.uploadFileName ? book.uploadFileName : book.url)));
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
touchTime,
|
||||||
|
loadTime,
|
||||||
|
desc: {
|
||||||
|
author,
|
||||||
|
title,
|
||||||
|
perc,
|
||||||
|
textLen,
|
||||||
|
},
|
||||||
|
readPart,
|
||||||
|
url: book.url,
|
||||||
|
path: book.path,
|
||||||
|
fullTitle: bt.fullTitle,
|
||||||
|
key: book.key,
|
||||||
|
sameBookKey: book.sameBookKey,
|
||||||
|
active: (activeBook.key == book.key),
|
||||||
|
activeParent: false,
|
||||||
|
inGroup: false,
|
||||||
|
|
||||||
|
//для сортировки
|
||||||
|
loadTimeRaw,
|
||||||
|
touchTimeRaw: book.touchTime,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const bt = utils.getBookTitle(book.fb2);
|
//нумерация
|
||||||
|
result.sort((a, b) => b.loadTimeRaw - a.loadTimeRaw);
|
||||||
|
let num = 0;
|
||||||
|
for (let i = result.length - 1; i >= 0; i--) {
|
||||||
|
num++;
|
||||||
|
result[i].num = num;
|
||||||
|
}
|
||||||
|
|
||||||
let title = bt.bookTitle;
|
//фильтрация
|
||||||
title = (title ? `"${title}"`: '');
|
const search = this.search;
|
||||||
const author = (bt.author ? bt.author : (bt.bookTitle ? bt.bookTitle : book.url));
|
if (search) {
|
||||||
|
result = result.filter(item => {
|
||||||
|
return !search ||
|
||||||
|
item.touchTime.includes(search) ||
|
||||||
|
item.loadTime.includes(search) ||
|
||||||
|
item.desc.title.toLowerCase().includes(search.toLowerCase()) ||
|
||||||
|
item.desc.author.toLowerCase().includes(search.toLowerCase())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
result.push({
|
//сортировка
|
||||||
num,
|
switch (this.sortMethod) {
|
||||||
touchDateTime: book.touchTime,
|
case 'loadTimeDesc':
|
||||||
touchDate: t[0],
|
result.sort((a, b) => b.loadTimeRaw - a.loadTimeRaw);
|
||||||
touchTime: t[1],
|
break;
|
||||||
desc: {
|
case 'loadTimeAsc':
|
||||||
author,
|
result.sort((a, b) => a.loadTimeRaw - b.loadTimeRaw);
|
||||||
title: `${title}${perc}${textLen}`,
|
break;
|
||||||
},
|
case 'touchTimeDesc':
|
||||||
readPart,
|
result.sort((a, b) => b.touchTimeRaw - a.touchTimeRaw);
|
||||||
descString: `${author}${title}${perc}${textLen}`,//для сортировки
|
break;
|
||||||
url: book.url,
|
case 'touchTimeAsc':
|
||||||
path: book.path,
|
result.sort((a, b) => a.touchTimeRaw - b.touchTimeRaw);
|
||||||
fullTitle: bt.fullTitle,
|
break;
|
||||||
key: book.key,
|
case 'authorDesc':
|
||||||
});
|
result.sort((a, b) => b.desc.author.localeCompare(a.desc.author));
|
||||||
|
break;
|
||||||
|
case 'authorAsc':
|
||||||
|
result.sort((a, b) => a.desc.author.localeCompare(b.desc.author));
|
||||||
|
break;
|
||||||
|
case 'titleDesc':
|
||||||
|
result.sort((a, b) => b.desc.title.localeCompare(a.desc.title));
|
||||||
|
break;
|
||||||
|
case 'titleAsc':
|
||||||
|
result.sort((a, b) => a.desc.title.localeCompare(b.desc.title));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//группировка
|
||||||
|
const groups = {};
|
||||||
|
const parents = {};
|
||||||
|
let newResult = [];
|
||||||
|
for (const book of result) {
|
||||||
|
if (book.sameBookKey !== undefined) {
|
||||||
|
if (!groups[book.sameBookKey]) {
|
||||||
|
groups[book.sameBookKey] = [];
|
||||||
|
parents[book.sameBookKey] = book;
|
||||||
|
|
||||||
|
book.group = groups[book.sameBookKey];
|
||||||
|
newResult.push(book);
|
||||||
|
} else {
|
||||||
|
book.inGroup = true;
|
||||||
|
if (book.active)
|
||||||
|
parents[book.sameBookKey].activeParent = true;
|
||||||
|
|
||||||
|
groups[book.sameBookKey].push(book);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newResult.push(book);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = newResult;
|
||||||
|
|
||||||
|
//showSameBook
|
||||||
|
if (this.showSameBook) {
|
||||||
|
newResult = [];
|
||||||
|
for (const book of result) {
|
||||||
|
newResult.push(book);
|
||||||
|
if (book.group) {
|
||||||
|
for (const sameBook of book.group) {
|
||||||
|
newResult.push(sameBook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = newResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
//другие стадии
|
||||||
|
//.....
|
||||||
|
|
||||||
|
this.tableData = result;
|
||||||
|
} finally {
|
||||||
|
this.lock.ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
const search = this.search;
|
|
||||||
result = result.filter(item => {
|
|
||||||
return !search ||
|
|
||||||
item.touchTime.includes(search) ||
|
|
||||||
item.touchDate.includes(search) ||
|
|
||||||
item.desc.title.toLowerCase().includes(search.toLowerCase()) ||
|
|
||||||
item.desc.author.toLowerCase().includes(search.toLowerCase())
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tableData = result;
|
|
||||||
this.updating = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resetSearch() {
|
resetSearch() {
|
||||||
@@ -266,19 +404,22 @@ class RecentBooksPage {
|
|||||||
this.$refs.input.focus();
|
this.$refs.input.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
wordEnding(num) {
|
wordEnding(num, type = 0) {
|
||||||
const endings = ['', 'а', 'и', 'и', 'и', '', '', '', '', ''];
|
const endings = [
|
||||||
|
['ов', '', 'а', 'а', 'а', 'ов', 'ов', 'ов', 'ов', 'ов'],
|
||||||
|
['й', 'я', 'и', 'и', 'и', 'й', 'й', 'й', 'й', 'й']
|
||||||
|
];
|
||||||
const deci = num % 100;
|
const deci = num % 100;
|
||||||
if (deci > 10 && deci < 20) {
|
if (deci > 10 && deci < 20) {
|
||||||
return '';
|
return endings[type][0];
|
||||||
} else {
|
} else {
|
||||||
return endings[num % 10];
|
return endings[type][num % 10];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get header() {
|
get header() {
|
||||||
const len = (this.tableData ? this.tableData.length : 0);
|
const len = (this.tableData ? this.tableData.length : 0);
|
||||||
return `${(this.search ? 'Найдено' : 'Всего')} ${len} книг${this.wordEnding(len)}`;
|
return `${(this.search ? 'Найдено' : 'Всего')} ${len} файл${this.wordEnding(len)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadBook(fb2path, fullTitle) {
|
async downloadBook(fb2path, fullTitle) {
|
||||||
@@ -311,8 +452,8 @@ class RecentBooksPage {
|
|||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadBook(url) {
|
loadBook(row) {
|
||||||
this.$emit('load-book', {url});
|
this.$emit('load-book', {url: row.url, path: row.path});
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,6 +464,111 @@ class RecentBooksPage {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showBar() {
|
||||||
|
this.lastScrollTop1 = this.$refs.vsContainer.scrollTop;
|
||||||
|
this.$refs.header.style.position = 'sticky';
|
||||||
|
this.$refs.header.style.top = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
onScroll() {
|
||||||
|
const curScrollTop = this.$refs.vsContainer.scrollTop;
|
||||||
|
|
||||||
|
if (this.lockScroll) {
|
||||||
|
this.lastScrollTop1 = curScrollTop;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curScrollTop - this.lastScrollTop1 > 100) {
|
||||||
|
this.$refs.header.style.top = `-${this.$refs.header.offsetHeight}px`;
|
||||||
|
this.$refs.header.style.transition = 'top 0.2s ease 0s';
|
||||||
|
|
||||||
|
this.lastScrollTop1 = curScrollTop;
|
||||||
|
} else if (curScrollTop - this.lastScrollTop2 < 0) {
|
||||||
|
this.$refs.header.style.position = 'sticky';
|
||||||
|
this.$refs.header.style.top = 0;
|
||||||
|
|
||||||
|
this.lastScrollTop1 = curScrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastScrollTop2 = curScrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
showSameBookClick() {
|
||||||
|
this.showSameBook = !this.showSameBook;
|
||||||
|
|
||||||
|
const newSettings = _.cloneDeep(this.settings);
|
||||||
|
newSettings.recentShowSameBook = this.showSameBook;
|
||||||
|
this.commit('reader/setSettings', newSettings);
|
||||||
|
|
||||||
|
this.updateTableData();
|
||||||
|
}
|
||||||
|
|
||||||
|
sortMethodSelected() {
|
||||||
|
const newSettings = _.cloneDeep(this.settings);
|
||||||
|
newSettings.recentSortMethod = this.sortMethod;
|
||||||
|
this.commit('reader/setSettings', newSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
async scrollToActiveBook() {
|
||||||
|
this.lockScroll = true;
|
||||||
|
try {
|
||||||
|
let activeIndex = -1;
|
||||||
|
let activeParentIndex = -1;
|
||||||
|
for (let i = 0; i < this.tableData.length; i++) {
|
||||||
|
const book = this.tableData[i];
|
||||||
|
if (book.active)
|
||||||
|
activeIndex = i;
|
||||||
|
if (book.activeParent)
|
||||||
|
activeParentIndex = i;
|
||||||
|
|
||||||
|
if (activeIndex >= 0 && activeParentIndex >= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = (activeIndex >= 0 ? activeIndex : activeParentIndex);
|
||||||
|
if (index >= 0) {
|
||||||
|
this.$refs.virtualScroll.scrollTo(index, 'center');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await utils.sleep(100);
|
||||||
|
this.lockScroll = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async scrollToBegin() {
|
||||||
|
this.lockScroll = true;
|
||||||
|
try {
|
||||||
|
this.$refs.virtualScroll.scrollTo(0, 'center');
|
||||||
|
} finally {
|
||||||
|
await utils.sleep(100);
|
||||||
|
this.lockScroll = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async scrollToEnd() {
|
||||||
|
this.lockScroll = true;
|
||||||
|
try {
|
||||||
|
this.$refs.virtualScroll.scrollTo(this.tableData.length, 'center');
|
||||||
|
} finally {
|
||||||
|
await utils.sleep(100);
|
||||||
|
this.lockScroll = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
get sortMethodOptions() {
|
||||||
|
return [
|
||||||
|
{label: '<span style="font-size: 150%">↑</span> Время загрузки', value: 'loadTimeDesc'},
|
||||||
|
{label: '<span style="font-size: 150%">↓</span> Время загрузки', value: 'loadTimeAsc'},
|
||||||
|
{label: '<span style="font-size: 150%">↑</span> Время чтения', value: 'touchTimeDesc'},
|
||||||
|
{label: '<span style="font-size: 150%">↓</span> Время чтения', value: 'touchTimeAsc'},
|
||||||
|
{label: '<span style="font-size: 150%">↑</span> Автор', value: 'authorDesc'},
|
||||||
|
{label: '<span style="font-size: 150%">↓</span> Автор', value: 'authorAsc'},
|
||||||
|
{label: '<span style="font-size: 150%">↑</span> Название', value: 'titleDesc'},
|
||||||
|
{label: '<span style="font-size: 150%">↓</span> Название', value: 'titleAsc'},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.$emit('recent-books-close');
|
this.$emit('recent-books-close');
|
||||||
}
|
}
|
||||||
@@ -340,29 +586,33 @@ export default vueComponent(RecentBooksPage);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.recent-books-table {
|
.recent-books-scroll {
|
||||||
width: 600px;
|
width: 573px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scroll-header {
|
||||||
|
height: 50px;
|
||||||
|
position: sticky;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
border-bottom: 2px solid #aaaaaa;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-row {
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-part {
|
||||||
|
padding: 4px 4px 4px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.clickable {
|
.clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.td-mp {
|
|
||||||
margin: 0 !important;
|
|
||||||
padding: 4px 4px 4px 4px !important;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-mp {
|
|
||||||
margin: 0 !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
border: 0;
|
|
||||||
border-left: 1px solid #ddd !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.break-word {
|
.break-word {
|
||||||
line-height: 180%;
|
line-height: 180%;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
@@ -370,25 +620,55 @@ export default vueComponent(RecentBooksPage);
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.even {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-book {
|
||||||
|
background-color: #b0f0b0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-parent-book {
|
||||||
|
background-color: #ffbbbb !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanded-icon {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-button {
|
||||||
|
min-width: 30px;
|
||||||
|
width: 30px;
|
||||||
|
min-height: 30px;
|
||||||
|
height: 30px;
|
||||||
|
margin: 10px 6px 0px 3px;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-info-bottom {
|
||||||
|
line-height: 110%;
|
||||||
|
border-left: 1px solid #cccccc;
|
||||||
|
border-bottom: 1px solid #cccccc;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-info-top {
|
||||||
|
line-height: 110%;
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
border-right: 0;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-info, .row-info-top {
|
||||||
|
color: #888888;
|
||||||
|
}
|
||||||
|
|
||||||
.read-bar {
|
.read-bar {
|
||||||
height: 3px;
|
height: 6px;
|
||||||
background-color: #aaaaaa;
|
background-color: #bbbbbb;
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.recent-books-table .q-table__middle {
|
|
||||||
height: 100%;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recent-books-table thead tr:first-child th {
|
|
||||||
position: sticky;
|
|
||||||
z-index: 1;
|
|
||||||
top: 0;
|
|
||||||
background-color: #c1f4cd;
|
|
||||||
}
|
|
||||||
.recent-books-table tr:nth-child(even) {
|
|
||||||
background-color: #f8f8f8;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -8,12 +8,10 @@
|
|||||||
<span v-show="initStep">{{ initPercentage }}%</span>
|
<span v-show="initStep">{{ initPercentage }}%</span>
|
||||||
|
|
||||||
<div v-show="!initStep" class="input">
|
<div v-show="!initStep" class="input">
|
||||||
<!--input ref="input"
|
<q-input
|
||||||
placeholder="что ищем"
|
ref="input" v-model="needle"
|
||||||
:value="needle" @input="needle = $event.target.value"/-->
|
|
||||||
<q-input ref="input" v-model="needle"
|
|
||||||
class="col" outlined dense
|
class="col" outlined dense
|
||||||
placeholder="что ищем"
|
placeholder="Найти"
|
||||||
@keydown="inputKeyDown"
|
@keydown="inputKeyDown"
|
||||||
/>
|
/>
|
||||||
<div style="position: absolute; right: 10px; margin-top: 10px; font-size: 16px;">
|
<div style="position: absolute; right: 10px; margin-top: 10px; font-size: 16px;">
|
||||||
@@ -108,7 +106,7 @@ class SearchPage {
|
|||||||
this.parsed = parsed;
|
this.parsed = parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.header = 'Найти';
|
this.header = 'Поиск в тексте';
|
||||||
await this.$nextTick();
|
await this.$nextTick();
|
||||||
this.$refs.input.focus();
|
this.$refs.input.focus();
|
||||||
this.$refs.input.select();
|
this.$refs.input.select();
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
<div class="part-header">Показывать кнопки панели</div>
|
|
||||||
|
|
||||||
<div class="item row" v-for="item in toolButtons" :key="item.name" v-show="item.name != 'libs' || mode == 'liberama.top'">
|
|
||||||
<div class="label-3"></div>
|
|
||||||
<div class="col row">
|
|
||||||
<q-checkbox size="xs" v-model="showToolButton[item.name]" :label="rstore.readerActions[item.name]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Window ref="window" height="95%" width="600px" @close="close">
|
<Window ref="window" width="600px" @close="close">
|
||||||
<template #header>
|
<template #header>
|
||||||
Настройки
|
Настройки
|
||||||
</template>
|
</template>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<div v-show="tabsScrollable" class="q-pt-lg" />
|
<div v-show="tabsScrollable" class="q-pt-lg" />
|
||||||
<q-tab class="tab" name="profiles" icon="la la-users" label="Профили" />
|
<q-tab class="tab" name="profiles" icon="la la-users" label="Профили" />
|
||||||
<q-tab class="tab" name="view" icon="la la-eye" label="Вид" />
|
<q-tab class="tab" name="view" icon="la la-eye" label="Вид" />
|
||||||
<q-tab class="tab" name="buttons" icon="la la-grip-horizontal" label="Кнопки" />
|
<q-tab class="tab" name="toolbar" icon="la la-grip-horizontal" label="Панель" />
|
||||||
<q-tab class="tab" name="keys" icon="la la-gamepad" label="Управление" />
|
<q-tab class="tab" name="keys" icon="la la-gamepad" label="Управление" />
|
||||||
<q-tab class="tab" name="pagemove" icon="la la-school" label="Листание" />
|
<q-tab class="tab" name="pagemove" icon="la la-school" label="Листание" />
|
||||||
<q-tab class="tab" name="convert" icon="la la-magic" label="Конвертир." />
|
<q-tab class="tab" name="convert" icon="la la-magic" label="Конвертир." />
|
||||||
@@ -82,8 +82,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Кнопки ---------------------------------------------------------------------->
|
<!-- Кнопки ---------------------------------------------------------------------->
|
||||||
<div v-if="selectedTab == 'buttons'" class="fit tab-panel">
|
<div v-if="selectedTab == 'toolbar'" class="fit tab-panel">
|
||||||
@@include('./ButtonsTab.inc');
|
@@include('./ToolBarTab.inc');
|
||||||
</div>
|
</div>
|
||||||
<!-- Управление ------------------------------------------------------------------>
|
<!-- Управление ------------------------------------------------------------------>
|
||||||
<div v-if="selectedTab == 'keys'" class="fit column">
|
<div v-if="selectedTab == 'keys'" class="fit column">
|
||||||
@@ -702,11 +702,11 @@ export default vueComponent(SettingsPage);
|
|||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-1, .label-7 {
|
.label-1, .label-3, .label-7 {
|
||||||
width: 75px;
|
width: 75px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-2, .label-3, .label-4, .label-5 {
|
.label-2, .label-4, .label-5 {
|
||||||
width: 110px;
|
width: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
client/components/Reader/SettingsPage/ToolBarTab.inc
Normal file
18
client/components/Reader/SettingsPage/ToolBarTab.inc
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<div class="part-header">Отображение</div>
|
||||||
|
|
||||||
|
<div class="item row no-wrap">
|
||||||
|
<div class="label-3"></div>
|
||||||
|
<q-checkbox size="xs" v-model="toolBarHideOnScroll" label="Скрывать/показывать панель при прокрутке" >
|
||||||
|
<q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
|
||||||
|
Скрывать/показывть панель при прокрутке текста вперед/назад
|
||||||
|
</q-tooltip>
|
||||||
|
</q-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="part-header">Показывать кнопки</div>
|
||||||
|
|
||||||
|
<div class="item row no-wrap" v-for="item in toolButtons" :key="item.name" v-show="item.name != 'libs' || mode == 'liberama.top'">
|
||||||
|
<div class="label-3"></div>
|
||||||
|
<q-checkbox size="xs" v-model="showToolButton[item.name]" :label="rstore.readerActions[item.name]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
ref="input"
|
ref="input"
|
||||||
v-model="search"
|
v-model="search"
|
||||||
class="q-ml-sm col"
|
class="q-ml-sm col"
|
||||||
outlined dense rounded
|
outlined dense
|
||||||
bg-color="grey-4"
|
bg-color="grey-4"
|
||||||
placeholder="Найти"
|
placeholder="Найти"
|
||||||
@click.stop
|
@click.stop
|
||||||
|
|||||||
@@ -66,7 +66,14 @@ const componentOptions = {
|
|||||||
watch: {
|
watch: {
|
||||||
bookPos: function() {
|
bookPos: function() {
|
||||||
this.$emit('book-pos-changed', {bookPos: this.bookPos, bookPosSeen: this.bookPosSeen});
|
this.$emit('book-pos-changed', {bookPos: this.bookPos, bookPosSeen: this.bookPosSeen});
|
||||||
|
|
||||||
this.draw();
|
this.draw();
|
||||||
|
|
||||||
|
if (this.userBookPosChange) {
|
||||||
|
this.$emit('hide-tool-bar', {show: (this.bookPos == 0 || this.bookPos < this.prevBookPos)});
|
||||||
|
this.prevBookPos = this.bookPos;
|
||||||
|
this.userBookPosChange = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
bookPosSeen: function() {
|
bookPosSeen: function() {
|
||||||
this.$emit('book-pos-changed', {bookPos: this.bookPos, bookPosSeen: this.bookPosSeen});
|
this.$emit('book-pos-changed', {bookPos: this.bookPos, bookPosSeen: this.bookPosSeen});
|
||||||
@@ -99,6 +106,8 @@ class TextPage {
|
|||||||
lastBook = null;
|
lastBook = null;
|
||||||
bookPos = 0;
|
bookPos = 0;
|
||||||
bookPosSeen = null;
|
bookPosSeen = null;
|
||||||
|
prevBookPos = 0;
|
||||||
|
userBookPosChange = false;
|
||||||
|
|
||||||
fontStyle = null;
|
fontStyle = null;
|
||||||
fontSize = null;
|
fontSize = null;
|
||||||
@@ -155,7 +164,7 @@ class TextPage {
|
|||||||
|
|
||||||
this.$root.addEventHook('resize', async() => {
|
this.$root.addEventHook('resize', async() => {
|
||||||
this.$nextTick(this.onResize);
|
this.$nextTick(this.onResize);
|
||||||
await utils.sleep(500);
|
await utils.sleep(200);
|
||||||
this.$nextTick(this.onResize);
|
this.$nextTick(this.onResize);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -499,12 +508,25 @@ class TextPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onResize() {
|
async onResize() {
|
||||||
|
if (this.resizing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.resizing = true;
|
||||||
try {
|
try {
|
||||||
|
const scrolled = this.doingScrolling;
|
||||||
|
if (scrolled)
|
||||||
|
await this.stopTextScrolling();
|
||||||
|
|
||||||
this.calcDrawProps();
|
this.calcDrawProps();
|
||||||
this.setBackground();
|
this.setBackground();
|
||||||
this.draw();
|
this.draw();
|
||||||
|
|
||||||
|
if (scrolled)
|
||||||
|
this.startTextScrolling();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//
|
//
|
||||||
|
} finally {
|
||||||
|
this.resizing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,7 +674,7 @@ class TextPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.book && this.bookPos > 0 && this.bookPos >= this.parsed.textLength) {
|
if (this.book && this.bookPos > 0 && this.bookPos >= this.parsed.textLength) {
|
||||||
this.doEnd(true);
|
this.doEnd(true, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,7 +697,7 @@ class TextPage {
|
|||||||
this.debouncedDrawPageDividerAndOrnament();
|
this.debouncedDrawPageDividerAndOrnament();
|
||||||
|
|
||||||
if (this.book && this.linesDown && this.linesDown.length < this.pageLineCount) {
|
if (this.book && this.linesDown && this.linesDown.length < this.pageLineCount) {
|
||||||
this.doEnd(true);
|
this.doEnd(true, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -911,12 +933,14 @@ class TextPage {
|
|||||||
|
|
||||||
doDown() {
|
doDown() {
|
||||||
if (this.linesDown && this.linesDown.length > this.pageLineCount && this.pageLineCount > 0) {
|
if (this.linesDown && this.linesDown.length > this.pageLineCount && this.pageLineCount > 0) {
|
||||||
|
this.userBookPosChange = true;
|
||||||
this.bookPos = this.linesDown[1].begin;
|
this.bookPos = this.linesDown[1].begin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doUp() {
|
doUp() {
|
||||||
if (this.linesUp && this.linesUp.length > 1 && this.pageLineCount > 0) {
|
if (this.linesUp && this.linesUp.length > 1 && this.pageLineCount > 0) {
|
||||||
|
this.userBookPosChange = true;
|
||||||
this.bookPos = this.linesUp[1].begin;
|
this.bookPos = this.linesUp[1].begin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -929,6 +953,7 @@ class TextPage {
|
|||||||
if (i >= 0 && this.linesDown.length >= 2*i + (this.keepLastToFirst ? 1 : 0)) {
|
if (i >= 0 && this.linesDown.length >= 2*i + (this.keepLastToFirst ? 1 : 0)) {
|
||||||
this.currentAnimation = this.pageChangeAnimation;
|
this.currentAnimation = this.pageChangeAnimation;
|
||||||
this.pageChangeDirectionDown = true;
|
this.pageChangeDirectionDown = true;
|
||||||
|
this.userBookPosChange = true;
|
||||||
this.bookPos = this.linesDown[i].begin;
|
this.bookPos = this.linesDown[i].begin;
|
||||||
} else
|
} else
|
||||||
this.doEnd();
|
this.doEnd();
|
||||||
@@ -944,6 +969,7 @@ class TextPage {
|
|||||||
if (i >= 0 && this.linesUp.length > i) {
|
if (i >= 0 && this.linesUp.length > i) {
|
||||||
this.currentAnimation = this.pageChangeAnimation;
|
this.currentAnimation = this.pageChangeAnimation;
|
||||||
this.pageChangeDirectionDown = false;
|
this.pageChangeDirectionDown = false;
|
||||||
|
this.userBookPosChange = true;
|
||||||
this.bookPos = this.linesUp[i].begin;
|
this.bookPos = this.linesUp[i].begin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -952,10 +978,11 @@ class TextPage {
|
|||||||
doHome() {
|
doHome() {
|
||||||
this.currentAnimation = this.pageChangeAnimation;
|
this.currentAnimation = this.pageChangeAnimation;
|
||||||
this.pageChangeDirectionDown = false;
|
this.pageChangeDirectionDown = false;
|
||||||
|
this.userBookPosChange = true;
|
||||||
this.bookPos = 0;
|
this.bookPos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
doEnd(noAni) {
|
doEnd(noAni, isUser = true) {
|
||||||
if (this.parsed.para.length && this.pageLineCount > 0) {
|
if (this.parsed.para.length && this.pageLineCount > 0) {
|
||||||
let i = this.parsed.para.length - 1;
|
let i = this.parsed.para.length - 1;
|
||||||
let lastPos = this.parsed.para[i].offset + this.parsed.para[i].length - 1;
|
let lastPos = this.parsed.para[i].offset + this.parsed.para[i].length - 1;
|
||||||
@@ -966,6 +993,7 @@ class TextPage {
|
|||||||
if (!noAni)
|
if (!noAni)
|
||||||
this.currentAnimation = this.pageChangeAnimation;
|
this.currentAnimation = this.pageChangeAnimation;
|
||||||
this.pageChangeDirectionDown = true;
|
this.pageChangeDirectionDown = true;
|
||||||
|
this.userBookPosChange = isUser;
|
||||||
this.bookPos = lines[i].begin;
|
this.bookPos = lines[i].begin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import sax from '../../../../server/core/sax';
|
|||||||
import * as utils from '../../../share/utils';
|
import * as utils from '../../../share/utils';
|
||||||
|
|
||||||
const maxImageLineCount = 100;
|
const maxImageLineCount = 100;
|
||||||
|
const maxParaTextLength = 10000;
|
||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
const defaultSettings = {
|
const defaultSettings = {
|
||||||
@@ -226,13 +227,25 @@ export default class BookParser {
|
|||||||
paraOffset += len;
|
paraOffset += len;
|
||||||
};
|
};
|
||||||
|
|
||||||
const growParagraph = (text, len) => {
|
const growParagraph = (text, len, textRaw) => {
|
||||||
if (paraIndex < 0) {
|
if (paraIndex < 0) {
|
||||||
newParagraph();
|
newParagraph();
|
||||||
growParagraph(text, len);
|
growParagraph(text, len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ограничение на размер куска текста в параграфе
|
||||||
|
if (textRaw && textRaw.length > maxParaTextLength) {
|
||||||
|
while (textRaw.length > 0) {
|
||||||
|
const textPart = textRaw.substring(0, maxParaTextLength);
|
||||||
|
textRaw = textRaw.substring(maxParaTextLength);
|
||||||
|
|
||||||
|
newParagraph();
|
||||||
|
growParagraph(textPart, textPart.length);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (inSubtitle) {
|
if (inSubtitle) {
|
||||||
curSubtitle.title += text;
|
curSubtitle.title += text;
|
||||||
} else if (inTitle) {
|
} else if (inTitle) {
|
||||||
@@ -536,7 +549,7 @@ export default class BookParser {
|
|||||||
tClose += (center ? '</center>' : '');
|
tClose += (center ? '</center>' : '');
|
||||||
|
|
||||||
if (text != ' ')
|
if (text != ' ')
|
||||||
growParagraph(`${tOpen}${text}${tClose}`, text.length);
|
growParagraph(`${tOpen}${text}${tClose}`, text.length, text);
|
||||||
else
|
else
|
||||||
growParagraph(' ', 1);
|
growParagraph(' ', 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import localForage from 'localforage';
|
import localForage from 'localforage';
|
||||||
|
import path from 'path-browserify';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import * as utils from '../../../share/utils';
|
import * as utils from '../../../share/utils';
|
||||||
import BookParser from './BookParser';
|
import BookParser from './BookParser';
|
||||||
|
|
||||||
const maxDataSize = 500*1024*1024;//compressed bytes
|
const maxDataSize = 500*1024*1024;//compressed bytes
|
||||||
|
const maxRecentLength = 5000;
|
||||||
|
|
||||||
//локальный кэш метаданных книг, ограничение maxDataSize
|
//локальный кэш метаданных книг, ограничение maxDataSize
|
||||||
const bmMetaStore = localForage.createInstance({
|
const bmMetaStore = localForage.createInstance({
|
||||||
@@ -17,9 +19,6 @@ const bmDataStore = localForage.createInstance({
|
|||||||
});
|
});
|
||||||
|
|
||||||
//список недавно открытых книг
|
//список недавно открытых книг
|
||||||
const bmRecentStoreOld = localForage.createInstance({
|
|
||||||
name: 'bmRecentStore'
|
|
||||||
});
|
|
||||||
const bmRecentStoreNew = localForage.createInstance({
|
const bmRecentStoreNew = localForage.createInstance({
|
||||||
name: 'bmRecentStoreNew'
|
name: 'bmRecentStoreNew'
|
||||||
});
|
});
|
||||||
@@ -39,7 +38,7 @@ class BookManager {
|
|||||||
|
|
||||||
this.saveRecentItem = _.debounce(() => {
|
this.saveRecentItem = _.debounce(() => {
|
||||||
bmRecentStoreNew.setItem('recent-item', this.recentItem);
|
bmRecentStoreNew.setItem('recent-item', this.recentItem);
|
||||||
this.recentRev = (this.recentRev < 1000 ? this.recentRev + 1 : 1);
|
this.recentRev = (this.recentRev < maxRecentLength ? this.recentRev + 1 : 1);
|
||||||
bmRecentStoreNew.setItem('rev', this.recentRev);
|
bmRecentStoreNew.setItem('rev', this.recentRev);
|
||||||
}, 200, {maxWait: 300});
|
}, 200, {maxWait: 300});
|
||||||
|
|
||||||
@@ -54,6 +53,9 @@ class BookManager {
|
|||||||
if (this.recentItem)
|
if (this.recentItem)
|
||||||
this.recent[this.recentItem.key] = this.recentItem;
|
this.recent[this.recentItem.key] = this.recentItem;
|
||||||
|
|
||||||
|
//конвертируем в новые ключи
|
||||||
|
await this.convertRecent();
|
||||||
|
|
||||||
this.recentLastKey = await bmRecentStoreNew.getItem('recent-last-key');
|
this.recentLastKey = await bmRecentStoreNew.getItem('recent-last-key');
|
||||||
if (this.recentLastKey) {
|
if (this.recentLastKey) {
|
||||||
const meta = await bmMetaStore.getItem(`bmMeta-${this.recentLastKey}`);
|
const meta = await bmMetaStore.getItem(`bmMeta-${this.recentLastKey}`);
|
||||||
@@ -70,6 +72,40 @@ class BookManager {
|
|||||||
this.loadStored();//no await
|
this.loadStored();//no await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: убрать в 2025г
|
||||||
|
async convertRecent() {
|
||||||
|
const converted = await bmRecentStoreNew.getItem('recent-converted');
|
||||||
|
|
||||||
|
if (converted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const newRecent = {};
|
||||||
|
for (const book of Object.values(this.recent)) {
|
||||||
|
|
||||||
|
if (!book.path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newKey = this.keyFromPath(book.path);
|
||||||
|
|
||||||
|
newRecent[newKey] = _.cloneDeep(book);
|
||||||
|
newRecent[newKey].key = newKey;
|
||||||
|
if (!newRecent[newKey].loadTime)
|
||||||
|
newRecent[newKey].loadTime = newRecent[newKey].addTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.recent = newRecent;
|
||||||
|
|
||||||
|
//console.log(converted);
|
||||||
|
(async() => {
|
||||||
|
await utils.sleep(3000);
|
||||||
|
this.saveRecent();
|
||||||
|
this.emit('recent-changed');
|
||||||
|
this.emit('set-recent');
|
||||||
|
await bmRecentStoreNew.setItem('recent-converted', true);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
//Ленивая асинхронная загрузка bmMetaStore
|
//Ленивая асинхронная загрузка bmMetaStore
|
||||||
async loadStored() {
|
async loadStored() {
|
||||||
//даем время для загрузки последней читаемой книги, чтобы не блокировать приложение
|
//даем время для загрузки последней читаемой книги, чтобы не блокировать приложение
|
||||||
@@ -196,8 +232,8 @@ class BookManager {
|
|||||||
|
|
||||||
async addBook(newBook, callback) {
|
async addBook(newBook, callback) {
|
||||||
let meta = {url: newBook.url, path: newBook.path};
|
let meta = {url: newBook.url, path: newBook.path};
|
||||||
meta.key = this.keyFromUrl(meta.url);
|
meta.key = this.keyFromPath(meta.path);
|
||||||
meta.addTime = Date.now();
|
meta.addTime = Date.now();//время добавления в кеш
|
||||||
|
|
||||||
const cb = (perc) => {
|
const cb = (perc) => {
|
||||||
const p = Math.round(30*perc/100);
|
const p = Math.round(30*perc/100);
|
||||||
@@ -232,10 +268,10 @@ class BookManager {
|
|||||||
async hasBookParsed(meta) {
|
async hasBookParsed(meta) {
|
||||||
if (!this.books)
|
if (!this.books)
|
||||||
return false;
|
return false;
|
||||||
if (!meta.url)
|
if (!meta.path)
|
||||||
return false;
|
return false;
|
||||||
if (!meta.key)
|
if (!meta.key)
|
||||||
meta.key = this.keyFromUrl(meta.url);
|
meta.key = this.keyFromPath(meta.path);
|
||||||
|
|
||||||
let book = this.books[meta.key];
|
let book = this.books[meta.key];
|
||||||
|
|
||||||
@@ -250,8 +286,12 @@ class BookManager {
|
|||||||
|
|
||||||
async getBook(meta, callback) {
|
async getBook(meta, callback) {
|
||||||
let result = undefined;
|
let result = undefined;
|
||||||
|
|
||||||
|
if (!meta.path)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!meta.key)
|
if (!meta.key)
|
||||||
meta.key = this.keyFromUrl(meta.url);
|
meta.key = this.keyFromPath(meta.path);
|
||||||
|
|
||||||
result = this.books[meta.key];
|
result = this.books[meta.key];
|
||||||
|
|
||||||
@@ -261,11 +301,6 @@ class BookManager {
|
|||||||
this.books[meta.key] = result;
|
this.books[meta.key] = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Если файл на сервере изменился, считаем, что в кеше его нету
|
|
||||||
if (meta.path && result && meta.path != result.path) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result && !result.parsed) {
|
if (result && !result.parsed) {
|
||||||
let data = await bmDataStore.getItem(`bmData-${meta.key}`);
|
let data = await bmDataStore.getItem(`bmData-${meta.key}`);
|
||||||
callback(5);
|
callback(5);
|
||||||
@@ -325,10 +360,20 @@ class BookManager {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
keyFromUrl(url) {
|
/*keyFromUrl(url) {
|
||||||
return utils.stringToHex(url);
|
return utils.stringToHex(url);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
keyFromPath(bookPath) {
|
||||||
|
return path.basename(bookPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keysEqual(bookPath1, bookPath2) {
|
||||||
|
if (bookPath1 === undefined || bookPath2 === undefined)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (this.keyFromPath(bookPath1) === this.keyFromPath(bookPath2));
|
||||||
|
}
|
||||||
//-- recent --------------------------------------------------------------
|
//-- recent --------------------------------------------------------------
|
||||||
async recentSetItem(item = null, skipCheck = false) {
|
async recentSetItem(item = null, skipCheck = false) {
|
||||||
const rev = await bmRecentStoreNew.getItem('rev');
|
const rev = await bmRecentStoreNew.getItem('rev');
|
||||||
@@ -369,7 +414,10 @@ class BookManager {
|
|||||||
|
|
||||||
async setRecentBook(value) {
|
async setRecentBook(value) {
|
||||||
let result = this.metaOnly(value);
|
let result = this.metaOnly(value);
|
||||||
result.touchTime = Date.now();
|
result.touchTime = Date.now();//время последнего чтения
|
||||||
|
if (!result.loadTime)
|
||||||
|
result.loadTime = Date.now();//время загрузки файла
|
||||||
|
|
||||||
result.deleted = 0;
|
result.deleted = 0;
|
||||||
|
|
||||||
if (this.recent[result.key]) {
|
if (this.recent[result.key]) {
|
||||||
@@ -401,7 +449,7 @@ class BookManager {
|
|||||||
const sorted = this.getSortedRecent();
|
const sorted = this.getSortedRecent();
|
||||||
|
|
||||||
let isDel = false;
|
let isDel = false;
|
||||||
for (let i = 1000; i < sorted.length; i++) {
|
for (let i = maxRecentLength; i < sorted.length; i++) {
|
||||||
delete this.recent[sorted[i].key];
|
delete this.recent[sorted[i].key];
|
||||||
isDel = true;
|
isDel = true;
|
||||||
}
|
}
|
||||||
@@ -421,7 +469,7 @@ class BookManager {
|
|||||||
|
|
||||||
let max = 0;
|
let max = 0;
|
||||||
let result = null;
|
let result = null;
|
||||||
for (let key in this.recent) {
|
for (const key in this.recent) {
|
||||||
const book = this.recent[key];
|
const book = this.recent[key];
|
||||||
if (!book.deleted && book.touchTime > max) {
|
if (!book.deleted && book.touchTime > max) {
|
||||||
max = book.touchTime;
|
max = book.touchTime;
|
||||||
@@ -452,6 +500,43 @@ class BookManager {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
findRecentByUrlAndPath(url, bookPath) {
|
||||||
|
if (bookPath) {
|
||||||
|
const key = this.keyFromPath(bookPath);
|
||||||
|
const book = this.recent[key];
|
||||||
|
if (book && !book.deleted)
|
||||||
|
return book;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max = 0;
|
||||||
|
let result = null;
|
||||||
|
|
||||||
|
for (const key in this.recent) {
|
||||||
|
const book = this.recent[key];
|
||||||
|
if (!book.deleted && book.url == url && book.loadTime > max) {
|
||||||
|
max = book.loadTime;
|
||||||
|
result = book;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
findRecentBySameBookKey(sameKey) {
|
||||||
|
let max = 0;
|
||||||
|
let result = null;
|
||||||
|
|
||||||
|
for (const key in this.recent) {
|
||||||
|
const book = this.recent[key];
|
||||||
|
if (!book.deleted && book.sameBookKey == sameKey && book.loadTime > max) {
|
||||||
|
max = book.loadTime;
|
||||||
|
result = book;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
async setRecent(value) {
|
async setRecent(value) {
|
||||||
const mergedRecent = _.cloneDeep(this.recent);
|
const mergedRecent = _.cloneDeep(this.recent);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,26 @@
|
|||||||
export const versionHistory = [
|
export const versionHistory = [
|
||||||
|
{
|
||||||
|
version: '0.11.7',
|
||||||
|
releaseDate: '2022-07-12',
|
||||||
|
showUntil: '2022-07-19',
|
||||||
|
content:
|
||||||
|
`
|
||||||
|
<ul>
|
||||||
|
<li>добавлено автосокрытие панели управления при листании, отключается в настройках</li>
|
||||||
|
<li>изменения в окне загруженных книг:</li>
|
||||||
|
<ul>
|
||||||
|
<li>добавлена группировка по версиям файла одной и той же книги</li>
|
||||||
|
<li>группировка происходит по имени загружаемого файла, либо по URL книги</li>
|
||||||
|
<li>добавлены различные методы сортировки списка загруженных книг</li>
|
||||||
|
<li>нумерация всегда осуществляется по времени загрузки</li>
|
||||||
|
</ul>
|
||||||
|
<li>незначительные общие изменения интерфейса, приведение к единому стилю</li>
|
||||||
|
<li>исправления багов</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
`
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
version: '0.11.6',
|
version: '0.11.6',
|
||||||
releaseDate: '2022-07-02',
|
releaseDate: '2022-07-02',
|
||||||
|
|||||||
@@ -55,6 +55,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--------------------------------------------------->
|
||||||
|
<div v-show="type == 'askYesNo'" class="bg-white no-wrap">
|
||||||
|
<div class="header row">
|
||||||
|
<div class="caption col row items-center q-ml-md">
|
||||||
|
<q-icon v-show="caption" class="q-mr-sm" :class="iconColor" :name="iconName" size="28px"></q-icon>
|
||||||
|
<div v-html="caption"></div>
|
||||||
|
</div>
|
||||||
|
<div class="close-icon column justify-center items-center">
|
||||||
|
<q-btn v-close-popup flat round dense>
|
||||||
|
<q-icon name="la la-times" size="18px"></q-icon>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="q-mx-md">
|
||||||
|
<div v-html="message"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="buttons row justify-end q-pa-md">
|
||||||
|
<q-btn v-close-popup class="q-px-md q-ml-sm" dense no-caps>
|
||||||
|
Нет
|
||||||
|
</q-btn>
|
||||||
|
<q-btn class="q-px-md q-ml-sm" color="primary" dense no-caps @click="okClick">
|
||||||
|
Да
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!--------------------------------------------------->
|
<!--------------------------------------------------->
|
||||||
<div v-show="type == 'prompt'" class="bg-white no-wrap">
|
<div v-show="type == 'prompt'" class="bg-white no-wrap">
|
||||||
<div class="header row">
|
<div class="header row">
|
||||||
@@ -262,6 +290,23 @@ class StdDialog {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
askYesNo(message, caption, opts) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.init(message, caption, opts);
|
||||||
|
|
||||||
|
this.hideTrigger = () => {
|
||||||
|
if (this.ok) {
|
||||||
|
resolve(true);
|
||||||
|
} else {
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.type = 'askYesNo';
|
||||||
|
this.active = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
prompt(message, caption, opts) {
|
prompt(message, caption, opts) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
this.enableValidator = false;
|
this.enableValidator = false;
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export default vueComponent(Window);
|
|||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
background: linear-gradient(to bottom right, green, #59B04F);
|
background: linear-gradient(to bottom right, #007000, #59B04F);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
@@ -161,8 +161,8 @@ export default vueComponent(Window);
|
|||||||
.header-text {
|
.header-text {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
color: yellow;
|
color: #FFFFA0;
|
||||||
text-shadow: 2px 1px 5px black, 2px 2px 5px black;
|
text-shadow: 2px 2px 5px #005000, 2px 1px 5px #005000;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,8 @@ export default vueComponent(Window);
|
|||||||
}
|
}
|
||||||
|
|
||||||
.close-button:hover {
|
.close-button:hover {
|
||||||
background-color: #69C05F;
|
color: white;
|
||||||
|
background-color: #FF3030;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import {QPopupProxy} from 'quasar/src/components/popup-proxy';
|
|||||||
import {QDialog} from 'quasar/src/components/dialog';
|
import {QDialog} from 'quasar/src/components/dialog';
|
||||||
import {QChip} from 'quasar/src/components/chip';
|
import {QChip} from 'quasar/src/components/chip';
|
||||||
import {QTree} from 'quasar/src/components/tree';
|
import {QTree} from 'quasar/src/components/tree';
|
||||||
|
import {QVirtualScroll} from 'quasar/src/components/virtual-scroll';
|
||||||
|
|
||||||
//import {QExpansionItem} from 'quasar/src/components/expansion-item';
|
//import {QExpansionItem} from 'quasar/src/components/expansion-item';
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
@@ -62,6 +64,7 @@ const components = {
|
|||||||
QChip,
|
QChip,
|
||||||
QTree,
|
QTree,
|
||||||
//QExpansionItem,
|
//QExpansionItem,
|
||||||
|
QVirtualScroll,
|
||||||
};
|
};
|
||||||
|
|
||||||
//directives
|
//directives
|
||||||
|
|||||||
53
client/share/LockQueue.js
Normal file
53
client/share/LockQueue.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
class LockQueue {
|
||||||
|
constructor(queueSize) {
|
||||||
|
this.queueSize = queueSize;
|
||||||
|
this.freed = true;
|
||||||
|
this.waitingQueue = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
//async
|
||||||
|
get(take = true) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (this.freed) {
|
||||||
|
if (take)
|
||||||
|
this.freed = false;
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.waitingQueue.length < this.queueSize) {
|
||||||
|
this.waitingQueue.push({resolve, reject});
|
||||||
|
} else {
|
||||||
|
reject(new Error('Lock queue is too long'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ret() {
|
||||||
|
if (this.waitingQueue.length) {
|
||||||
|
this.waitingQueue.shift().resolve();
|
||||||
|
} else {
|
||||||
|
this.freed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//async
|
||||||
|
wait() {
|
||||||
|
return this.get(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
retAll() {
|
||||||
|
while (this.waitingQueue.length) {
|
||||||
|
this.waitingQueue.shift().resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errAll(error = 'rejected') {
|
||||||
|
while (this.waitingQueue.length) {
|
||||||
|
this.waitingQueue.shift().reject(new Error(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LockQueue;
|
||||||
@@ -21,7 +21,7 @@ const readerActions = {
|
|||||||
'offlineMode': 'Автономный режим (без интернета)',
|
'offlineMode': 'Автономный режим (без интернета)',
|
||||||
'contents': 'Оглавление/закладки',
|
'contents': 'Оглавление/закладки',
|
||||||
'libs': 'Сетевая библиотека',
|
'libs': 'Сетевая библиотека',
|
||||||
'recentBooks': 'Открыть недавние',
|
'recentBooks': 'Показать загруженные',
|
||||||
'switchToolbar': 'Показать/скрыть панель управления',
|
'switchToolbar': 'Показать/скрыть панель управления',
|
||||||
'donate': '',
|
'donate': '',
|
||||||
'bookBegin': 'В начало книги',
|
'bookBegin': 'В начало книги',
|
||||||
@@ -185,8 +185,12 @@ const settingDefaults = {
|
|||||||
|
|
||||||
fontShifts: {},
|
fontShifts: {},
|
||||||
showToolButton: {},
|
showToolButton: {},
|
||||||
|
toolBarHideOnScroll: true,
|
||||||
userHotKeys: {},
|
userHotKeys: {},
|
||||||
userWallpapers: [],
|
userWallpapers: [],
|
||||||
|
|
||||||
|
recentShowSameBook: false,
|
||||||
|
recentSortMethod: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const font of fonts)
|
for (const font of fonts)
|
||||||
@@ -222,9 +226,6 @@ const libsDefaults = {
|
|||||||
{r: 'http://flibusta.is', s: 'http://flibusta.is', list: [
|
{r: 'http://flibusta.is', s: 'http://flibusta.is', list: [
|
||||||
{l: 'http://flibusta.is', c: 'Флибуста | Книжное братство'},
|
{l: 'http://flibusta.is', c: 'Флибуста | Книжное братство'},
|
||||||
]},
|
]},
|
||||||
{r: 'https://flibs.in', s: 'https://flibs.in', list: [
|
|
||||||
{l: 'https://flibs.in', c: 'Flibs'},
|
|
||||||
]},
|
|
||||||
{r: 'http://fantasy-worlds.org', s: 'http://fantasy-worlds.org', list: [
|
{r: 'http://fantasy-worlds.org', s: 'http://fantasy-worlds.org', list: [
|
||||||
{l: 'http://fantasy-worlds.org', c: 'Миры Фэнтези'},
|
{l: 'http://fantasy-worlds.org', c: 'Миры Фэнтези'},
|
||||||
]},
|
]},
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ server {
|
|||||||
|
|
||||||
server_name liberama.top;
|
server_name liberama.top;
|
||||||
|
|
||||||
client_max_body_size 50m;
|
client_max_body_size 100m;
|
||||||
proxy_read_timeout 1h;
|
proxy_read_timeout 1h;
|
||||||
|
|
||||||
gzip on;
|
gzip on;
|
||||||
@@ -63,7 +63,7 @@ server {
|
|||||||
listen 80;
|
listen 80;
|
||||||
server_name b.liberama.top;
|
server_name b.liberama.top;
|
||||||
|
|
||||||
client_max_body_size 50m;
|
client_max_body_size 100m;
|
||||||
proxy_read_timeout 1h;
|
proxy_read_timeout 1h;
|
||||||
|
|
||||||
gzip on;
|
gzip on;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
if ! pgrep -x "liberama" > /dev/null ; then
|
if ! pgrep -x "liberama" > /dev/null ; then
|
||||||
sudo -H -u www-data bash -c "cd /var/www; /home/liberama/liberama"
|
sudo -H -u www-data bash -c "cd /var/www; /home/liberama/liberama >/dev/null"
|
||||||
else
|
else
|
||||||
echo "Process 'liberama' already running"
|
echo "Process 'liberama' already running"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ server {
|
|||||||
|
|
||||||
server_name omnireader.ru;
|
server_name omnireader.ru;
|
||||||
|
|
||||||
client_max_body_size 50m;
|
client_max_body_size 100m;
|
||||||
proxy_read_timeout 1h;
|
proxy_read_timeout 1h;
|
||||||
|
|
||||||
gzip on;
|
gzip on;
|
||||||
@@ -52,7 +52,7 @@ server {
|
|||||||
listen 80;
|
listen 80;
|
||||||
server_name old.omnireader.ru;
|
server_name old.omnireader.ru;
|
||||||
|
|
||||||
client_max_body_size 50m;
|
client_max_body_size 100m;
|
||||||
|
|
||||||
gzip on;
|
gzip on;
|
||||||
gzip_min_length 1024;
|
gzip_min_length 1024;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
sudo -H -u www-data bash -c "cd /var/www; /home/liberama/liberama" & disown
|
sudo -H -u www-data bash -c "cd /var/www; /home/liberama/liberama >/dev/null & disown"
|
||||||
sudo service cron start
|
sudo service cron start
|
||||||
|
|||||||
186
package-lock.json
generated
186
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "Liberama",
|
"name": "Liberama",
|
||||||
"version": "0.11.5",
|
"version": "0.11.7",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "Liberama",
|
"name": "Liberama",
|
||||||
"version": "0.11.5",
|
"version": "0.11.7",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -21,15 +21,15 @@
|
|||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"he": "^1.2.0",
|
"he": "^1.2.0",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"jembadb": "^3.0.6",
|
"jembadb": "^3.0.8",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"multer": "^1.4.3",
|
"multer": "^1.4.5-lts.1",
|
||||||
"pako": "^2.0.4",
|
"pako": "^2.0.4",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"pidusage": "^3.0.0",
|
"pidusage": "^3.0.0",
|
||||||
"quasar": "^2.3.2",
|
"quasar": "^2.7.5",
|
||||||
"safe-buffer": "^5.2.1",
|
"safe-buffer": "^5.2.1",
|
||||||
"sanitize-html": "^2.5.3",
|
"sanitize-html": "^2.5.3",
|
||||||
"sjcl": "^1.0.8",
|
"sjcl": "^1.0.8",
|
||||||
@@ -38,8 +38,8 @@
|
|||||||
"sqlite3": "^5.0.2",
|
"sqlite3": "^5.0.2",
|
||||||
"tar-fs": "^2.1.1",
|
"tar-fs": "^2.1.1",
|
||||||
"unbzip2-stream": "^1.4.3",
|
"unbzip2-stream": "^1.4.3",
|
||||||
"vue": "^3.2.22",
|
"vue": "^3.2.37",
|
||||||
"vue-router": "^4.0.12",
|
"vue-router": "^4.1.1",
|
||||||
"vuex": "^4.0.2",
|
"vuex": "^4.0.2",
|
||||||
"vuex-persistedstate": "^4.1.0",
|
"vuex-persistedstate": "^4.1.0",
|
||||||
"webdav": "^4.7.0",
|
"webdav": "^4.7.0",
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"css-loader": "^6.5.1",
|
"css-loader": "^6.5.1",
|
||||||
"css-minimizer-webpack-plugin": "^4.0.0",
|
"css-minimizer-webpack-plugin": "^4.0.0",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^8.19.0",
|
||||||
"eslint-plugin-vue": "^9.1.1",
|
"eslint-plugin-vue": "^9.2.0",
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"mini-css-extract-plugin": "^2.4.4",
|
"mini-css-extract-plugin": "^2.4.4",
|
||||||
"pkg": "^5.5.1",
|
"pkg": "^5.5.1",
|
||||||
@@ -3239,15 +3239,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/busboy": {
|
"node_modules/busboy": {
|
||||||
"version": "0.2.14",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||||
"integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==",
|
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dicer": "0.2.5",
|
"streamsearch": "^1.1.0"
|
||||||
"readable-stream": "1.1.x"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=10.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/byte-length": {
|
"node_modules/byte-length": {
|
||||||
@@ -4348,18 +4347,6 @@
|
|||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dicer": {
|
|
||||||
"version": "0.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
|
|
||||||
"integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==",
|
|
||||||
"dependencies": {
|
|
||||||
"readable-stream": "1.1.x",
|
|
||||||
"streamsearch": "0.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/dir-glob": {
|
"node_modules/dir-glob": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||||
@@ -4785,9 +4772,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-vue": {
|
"node_modules/eslint-plugin-vue": {
|
||||||
"version": "9.1.1",
|
"version": "9.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.2.0.tgz",
|
||||||
"integrity": "sha512-W9n5PB1X2jzC7CK6riG0oAcxjmKrjTF6+keL1rni8n57DZeilx/Fulz+IRJK3lYseLNAygN0I62L7DvioW40Tw==",
|
"integrity": "sha512-W2hc+NUXoce8sZtWgZ45miQTy6jNyuSdub5aZ1IBune4JDeAyzucYX0TzkrQ1jMO52sNUDYlCIHDoaNePe0p5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eslint-utils": "^3.0.0",
|
"eslint-utils": "^3.0.0",
|
||||||
@@ -6429,11 +6416,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/isarray": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
|
||||||
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
|
|
||||||
},
|
|
||||||
"node_modules/isexe": {
|
"node_modules/isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
@@ -6538,9 +6520,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jembadb": {
|
"node_modules/jembadb": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/jembadb/-/jembadb-3.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/jembadb/-/jembadb-3.0.8.tgz",
|
||||||
"integrity": "sha512-JRs6bTPDMq/UbkI2OweSSGpJX20JiUSQyBJjAOAn1ITPkFGr7q8F3ClEio1gPcmUJryy6EWMX+UnxDS6smJdJA==",
|
"integrity": "sha512-ukLawbH4IXsKeDTJWNmAEV7D9flQ0m3OMcDSbSjpFWimoyzSn7TyBhWSB8hqmM+9Dyqyh2/nZAcCBu2exXgirQ==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.4.0"
|
"node": ">=14.4.0"
|
||||||
}
|
}
|
||||||
@@ -7242,22 +7224,20 @@
|
|||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
"node_modules/multer": {
|
"node_modules/multer": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.5-lts.1",
|
||||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
|
||||||
"integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==",
|
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
|
||||||
"deprecated": "Multer 1.x is affected by CVE-2022-24434. This is fixed in v1.4.4-lts.1 which drops support for versions of Node.js before 6. Please upgrade to at least Node.js 6 and version 1.4.4-lts.1 of Multer. If you need support for older versions of Node.js, we are open to accepting patches that would fix the CVE on the main 1.x release line, whilst maintaining compatibility with Node.js 0.10.",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"append-field": "^1.0.0",
|
"append-field": "^1.0.0",
|
||||||
"busboy": "^0.2.11",
|
"busboy": "^1.0.0",
|
||||||
"concat-stream": "^1.5.2",
|
"concat-stream": "^1.5.2",
|
||||||
"mkdirp": "^0.5.4",
|
"mkdirp": "^0.5.4",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
"on-finished": "^2.3.0",
|
|
||||||
"type-is": "^1.6.4",
|
"type-is": "^1.6.4",
|
||||||
"xtend": "^4.0.0"
|
"xtend": "^4.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.10.0"
|
"node": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/multistream": {
|
"node_modules/multistream": {
|
||||||
@@ -8802,9 +8782,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/quasar": {
|
"node_modules/quasar": {
|
||||||
"version": "2.7.4",
|
"version": "2.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.7.5.tgz",
|
||||||
"integrity": "sha512-8OIa6azm7N6QUPjcZ5AhDCEBha5NnNqt+D1BMIteqaSqkVKFYBf+FMhUCC8R/Tc6Myz85vK7KGPn9tvaC6gXYQ==",
|
"integrity": "sha512-DWI0S+bXASfMSPrB8c/LVsXpA4dF7cBUbaJlcrM+1ioTNBHtiudma2Nhk2SDd5bzk9AYVHh5A8JCZuKqQAXt7g==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.18.1",
|
"node": ">= 10.18.1",
|
||||||
"npm": ">= 6.13.4",
|
"npm": ">= 6.13.4",
|
||||||
@@ -8924,17 +8904,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/readable-stream": {
|
|
||||||
"version": "1.1.14",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
|
||||||
"integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"core-util-is": "~1.0.0",
|
|
||||||
"inherits": "~2.0.1",
|
|
||||||
"isarray": "0.0.1",
|
|
||||||
"string_decoder": "~0.10.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rechoir": {
|
"node_modules/rechoir": {
|
||||||
"version": "0.7.1",
|
"version": "0.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
|
||||||
@@ -9735,18 +9704,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/streamsearch": {
|
"node_modules/streamsearch": {
|
||||||
"version": "0.1.2",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||||
"integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==",
|
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/string_decoder": {
|
|
||||||
"version": "0.10.31",
|
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
|
||||||
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
|
|
||||||
},
|
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
@@ -10611,11 +10575,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue-router": {
|
"node_modules/vue-router": {
|
||||||
"version": "4.0.16",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.16.tgz",
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.1.tgz",
|
||||||
"integrity": "sha512-JcO7cb8QJLBWE+DfxGUL3xUDOae/8nhM1KVdnudadTAORbuxIC/xAydC5Zr/VLHUDQi1ppuTF5/rjBGzgzrJNA==",
|
"integrity": "sha512-Wp1mEf2xCwT0ez7o9JvgpfBp9JGnVb+dPERzXDbugTatzJAJ60VWOhJKifQty85k+jOreoFHER4r5fu062PhPw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/devtools-api": "^6.0.0"
|
"@vue/devtools-api": "^6.1.4"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/posva"
|
"url": "https://github.com/sponsors/posva"
|
||||||
@@ -13960,12 +13924,11 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"busboy": {
|
"busboy": {
|
||||||
"version": "0.2.14",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||||
"integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==",
|
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"dicer": "0.2.5",
|
"streamsearch": "^1.1.0"
|
||||||
"readable-stream": "1.1.x"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"byte-length": {
|
"byte-length": {
|
||||||
@@ -14792,15 +14755,6 @@
|
|||||||
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"dicer": {
|
|
||||||
"version": "0.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
|
|
||||||
"integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==",
|
|
||||||
"requires": {
|
|
||||||
"readable-stream": "1.1.x",
|
|
||||||
"streamsearch": "0.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dir-glob": {
|
"dir-glob": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||||
@@ -15220,9 +15174,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-plugin-vue": {
|
"eslint-plugin-vue": {
|
||||||
"version": "9.1.1",
|
"version": "9.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.2.0.tgz",
|
||||||
"integrity": "sha512-W9n5PB1X2jzC7CK6riG0oAcxjmKrjTF6+keL1rni8n57DZeilx/Fulz+IRJK3lYseLNAygN0I62L7DvioW40Tw==",
|
"integrity": "sha512-W2hc+NUXoce8sZtWgZ45miQTy6jNyuSdub5aZ1IBune4JDeAyzucYX0TzkrQ1jMO52sNUDYlCIHDoaNePe0p5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"eslint-utils": "^3.0.0",
|
"eslint-utils": "^3.0.0",
|
||||||
@@ -16340,11 +16294,6 @@
|
|||||||
"call-bind": "^1.0.2"
|
"call-bind": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"isarray": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
|
||||||
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
|
|
||||||
},
|
|
||||||
"isexe": {
|
"isexe": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
@@ -16421,9 +16370,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jembadb": {
|
"jembadb": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/jembadb/-/jembadb-3.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/jembadb/-/jembadb-3.0.8.tgz",
|
||||||
"integrity": "sha512-JRs6bTPDMq/UbkI2OweSSGpJX20JiUSQyBJjAOAn1ITPkFGr7q8F3ClEio1gPcmUJryy6EWMX+UnxDS6smJdJA=="
|
"integrity": "sha512-ukLawbH4IXsKeDTJWNmAEV7D9flQ0m3OMcDSbSjpFWimoyzSn7TyBhWSB8hqmM+9Dyqyh2/nZAcCBu2exXgirQ=="
|
||||||
},
|
},
|
||||||
"jest-worker": {
|
"jest-worker": {
|
||||||
"version": "27.5.1",
|
"version": "27.5.1",
|
||||||
@@ -16980,16 +16929,15 @@
|
|||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
"multer": {
|
"multer": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.5-lts.1",
|
||||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
|
||||||
"integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==",
|
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"append-field": "^1.0.0",
|
"append-field": "^1.0.0",
|
||||||
"busboy": "^0.2.11",
|
"busboy": "^1.0.0",
|
||||||
"concat-stream": "^1.5.2",
|
"concat-stream": "^1.5.2",
|
||||||
"mkdirp": "^0.5.4",
|
"mkdirp": "^0.5.4",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
"on-finished": "^2.3.0",
|
|
||||||
"type-is": "^1.6.4",
|
"type-is": "^1.6.4",
|
||||||
"xtend": "^4.0.0"
|
"xtend": "^4.0.0"
|
||||||
}
|
}
|
||||||
@@ -18077,9 +18025,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"quasar": {
|
"quasar": {
|
||||||
"version": "2.7.4",
|
"version": "2.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/quasar/-/quasar-2.7.5.tgz",
|
||||||
"integrity": "sha512-8OIa6azm7N6QUPjcZ5AhDCEBha5NnNqt+D1BMIteqaSqkVKFYBf+FMhUCC8R/Tc6Myz85vK7KGPn9tvaC6gXYQ=="
|
"integrity": "sha512-DWI0S+bXASfMSPrB8c/LVsXpA4dF7cBUbaJlcrM+1ioTNBHtiudma2Nhk2SDd5bzk9AYVHh5A8JCZuKqQAXt7g=="
|
||||||
},
|
},
|
||||||
"querystring": {
|
"querystring": {
|
||||||
"version": "0.2.1",
|
"version": "0.2.1",
|
||||||
@@ -18158,17 +18106,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"readable-stream": {
|
|
||||||
"version": "1.1.14",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
|
||||||
"integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
|
|
||||||
"requires": {
|
|
||||||
"core-util-is": "~1.0.0",
|
|
||||||
"inherits": "~2.0.1",
|
|
||||||
"isarray": "0.0.1",
|
|
||||||
"string_decoder": "~0.10.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rechoir": {
|
"rechoir": {
|
||||||
"version": "0.7.1",
|
"version": "0.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
|
||||||
@@ -18773,14 +18710,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"streamsearch": {
|
"streamsearch": {
|
||||||
"version": "0.1.2",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||||
"integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA=="
|
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
|
||||||
},
|
|
||||||
"string_decoder": {
|
|
||||||
"version": "0.10.31",
|
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
|
||||||
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
|
|
||||||
},
|
},
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -19424,11 +19356,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vue-router": {
|
"vue-router": {
|
||||||
"version": "4.0.16",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.16.tgz",
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.1.tgz",
|
||||||
"integrity": "sha512-JcO7cb8QJLBWE+DfxGUL3xUDOae/8nhM1KVdnudadTAORbuxIC/xAydC5Zr/VLHUDQi1ppuTF5/rjBGzgzrJNA==",
|
"integrity": "sha512-Wp1mEf2xCwT0ez7o9JvgpfBp9JGnVb+dPERzXDbugTatzJAJ60VWOhJKifQty85k+jOreoFHER4r5fu062PhPw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/devtools-api": "^6.0.0"
|
"@vue/devtools-api": "^6.1.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vue-style-loader": {
|
"vue-style-loader": {
|
||||||
|
|||||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Liberama",
|
"name": "Liberama",
|
||||||
"version": "0.11.6",
|
"version": "0.11.7",
|
||||||
"author": "Book Pauk <bookpauk@gmail.com>",
|
"author": "Book Pauk <bookpauk@gmail.com>",
|
||||||
"license": "CC0-1.0",
|
"license": "CC0-1.0",
|
||||||
"repository": "bookpauk/liberama",
|
"repository": "bookpauk/liberama",
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"css-loader": "^6.5.1",
|
"css-loader": "^6.5.1",
|
||||||
"css-minimizer-webpack-plugin": "^4.0.0",
|
"css-minimizer-webpack-plugin": "^4.0.0",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^8.19.0",
|
||||||
"eslint-plugin-vue": "^9.1.1",
|
"eslint-plugin-vue": "^9.2.0",
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"mini-css-extract-plugin": "^2.4.4",
|
"mini-css-extract-plugin": "^2.4.4",
|
||||||
"pkg": "^5.5.1",
|
"pkg": "^5.5.1",
|
||||||
@@ -59,15 +59,15 @@
|
|||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"he": "^1.2.0",
|
"he": "^1.2.0",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"jembadb": "^3.0.6",
|
"jembadb": "^3.0.8",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"multer": "^1.4.3",
|
"multer": "^1.4.5-lts.1",
|
||||||
"pako": "^2.0.4",
|
"pako": "^2.0.4",
|
||||||
"path-browserify": "^1.0.1",
|
"path-browserify": "^1.0.1",
|
||||||
"pidusage": "^3.0.0",
|
"pidusage": "^3.0.0",
|
||||||
"quasar": "^2.3.2",
|
"quasar": "^2.7.5",
|
||||||
"safe-buffer": "^5.2.1",
|
"safe-buffer": "^5.2.1",
|
||||||
"sanitize-html": "^2.5.3",
|
"sanitize-html": "^2.5.3",
|
||||||
"sjcl": "^1.0.8",
|
"sjcl": "^1.0.8",
|
||||||
@@ -76,8 +76,8 @@
|
|||||||
"sqlite3": "^5.0.2",
|
"sqlite3": "^5.0.2",
|
||||||
"tar-fs": "^2.1.1",
|
"tar-fs": "^2.1.1",
|
||||||
"unbzip2-stream": "^1.4.3",
|
"unbzip2-stream": "^1.4.3",
|
||||||
"vue": "^3.2.22",
|
"vue": "^3.2.37",
|
||||||
"vue-router": "^4.0.12",
|
"vue-router": "^4.1.1",
|
||||||
"vuex": "^4.0.2",
|
"vuex": "^4.0.2",
|
||||||
"vuex-persistedstate": "^4.1.0",
|
"vuex-persistedstate": "^4.1.0",
|
||||||
"webdav": "^4.7.0",
|
"webdav": "^4.7.0",
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class BaseLog {
|
|||||||
this.outputBuffer = [];
|
this.outputBuffer = [];
|
||||||
|
|
||||||
await this.flushImpl(this.data)
|
await this.flushImpl(this.data)
|
||||||
.catch(e => { console.log(e); ayncExit.exit(1); } );
|
.catch(e => { console.error(`Logger error: ${e}`); ayncExit.exit(1); } );
|
||||||
this.flushing = false;
|
this.flushing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,6 +218,8 @@ class Logger {
|
|||||||
} else {
|
} else {
|
||||||
console.log(mes);
|
console.log(mes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return mes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async close() {
|
async close() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const chardet = require('chardet');
|
|||||||
function getEncoding(buf) {
|
function getEncoding(buf) {
|
||||||
let selected = getEncodingLite(buf);
|
let selected = getEncodingLite(buf);
|
||||||
|
|
||||||
if (selected == 'ISO-8859-5') {
|
if (selected == 'ISO-8859-5' && buf.length > 10) {
|
||||||
const charsetAll = chardet.analyse(buf.slice(0, 20000));
|
const charsetAll = chardet.analyse(buf.slice(0, 20000));
|
||||||
for (const charset of charsetAll) {
|
for (const charset of charsetAll) {
|
||||||
if (charset.name.indexOf('ISO-8859') < 0) {
|
if (charset.name.indexOf('ISO-8859') < 0) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ const _ = require('lodash');
|
|||||||
|
|
||||||
const utils = require('../utils');
|
const utils = require('../utils');
|
||||||
const JembaConnManager = require('../../db/JembaConnManager');//singleton
|
const JembaConnManager = require('../../db/JembaConnManager');//singleton
|
||||||
|
const log = new (require('../AppLogger'))().log;//singleton
|
||||||
|
|
||||||
let instance = null;
|
let instance = null;
|
||||||
|
|
||||||
@@ -20,25 +21,30 @@ class JembaReaderStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async doAction(act) {
|
async doAction(act) {
|
||||||
if (!_.isObject(act.items))
|
try {
|
||||||
throw new Error('items is not an object');
|
if (!_.isObject(act.items))
|
||||||
|
throw new Error('items is not an object');
|
||||||
|
|
||||||
let result = {};
|
let result = {};
|
||||||
switch (act.action) {
|
switch (act.action) {
|
||||||
case 'check':
|
case 'check':
|
||||||
result = await this.checkItems(act.items);
|
result = await this.checkItems(act.items);
|
||||||
break;
|
break;
|
||||||
case 'get':
|
case 'get':
|
||||||
result = await this.getItems(act.items);
|
result = await this.getItems(act.items);
|
||||||
break;
|
break;
|
||||||
case 'set':
|
case 'set':
|
||||||
result = await this.setItems(act.items, act.force);
|
result = await this.setItems(act.items, act.force);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown action');
|
throw new Error('Unknown action');
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
log(LM_ERR, `JembaReaderStorage: ${e.message}`);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkItems(items) {
|
async checkItems(items) {
|
||||||
|
|||||||
Reference in New Issue
Block a user