diff --git a/client/components/App.vue b/client/components/App.vue
index 51d135e4..70ae5c23 100644
--- a/client/components/App.vue
+++ b/client/components/App.vue
@@ -248,7 +248,7 @@ body, html, #app {
}
.dborder {
- border: 2px solid yellow !important;
+ border: 2px solid magenta !important;
}
.icon-rotate {
diff --git a/client/components/ExternalLibs/BookmarkSettings/BookmarkSettings.vue b/client/components/ExternalLibs/BookmarkSettings/BookmarkSettings.vue
index 97653dd7..e591c26b 100644
--- a/client/components/ExternalLibs/BookmarkSettings/BookmarkSettings.vue
+++ b/client/components/ExternalLibs/BookmarkSettings/BookmarkSettings.vue
@@ -4,6 +4,65 @@
Настроить закладки
+
+
+
+ Открыть выбранную закладку
+
+
+
+
+
+
+
+
+
+
+
+ Добавить закладку
+
+
+ Удалить отмеченные закладки
+
+
+ Редактировать закладку
+
+
+ Переместить отмеченные вверх
+
+
+ Переместить отмеченные вниз
+
+
+ Установить по умолчанию
+
+
+
+
+
+
+
+
+
+
+ {{ p.node.label }}
+
+
+
+
+
@@ -11,11 +70,16 @@
//-----------------------------------------------------------------------------
import Vue from 'vue';
import Component from 'vue-class-component';
+import _ from 'lodash';
+
import Window from '../../share/Window.vue';
+import * as lu from '../linkUtils';
+import rstore from '../../../store/modules/reader';
const BookmarkSettingsProps = Vue.extend({
props: {
libs: Object,
+ addBookmarkVisible: Boolean,
}
});
@@ -24,12 +88,20 @@ export default @Component({
Window,
},
watch: {
- libs: function() {
+ ticked: function() {
+ this.checkAllTicked();
},
}
})
class BookmarkSettings extends BookmarkSettingsProps {
+ search = '';
+ selected = '';
+ ticked = [];
+ expanded = [];
+ tickAll = false;
+
created() {
+ this.afterInit = true;
}
mounted() {
@@ -39,11 +111,193 @@ class BookmarkSettings extends BookmarkSettingsProps {
this.$refs.window.init();
}
+ get nodes() {
+ const result = [];
+
+ const expanded = [];
+ this.links = {};
+ this.libs.groups.forEach(group => {
+ const rkey = `r-${group.r}`;
+ const g = {label: group.r, key: rkey, children: []};
+ this.links[rkey] = {l: group.r, c: ''};
+
+ group.list.forEach(link => {
+ const key = link.l;
+ g.children.push({
+ label: (link.c ? link.c + ' ': '') + lu.removeOrigin(link.l),
+ key
+ });
+
+ this.links[key] = link;
+ if (link.l == this.libs.startLink && expanded.indexOf(rkey) < 0) {
+ expanded.push(rkey);
+ }
+
+ });
+
+ result.push(g);
+ });
+
+ if (this.afterInit) {
+ this.$nextTick(() => {
+ this.expanded = expanded;
+ });
+ this.afterInit = false;
+ }
+
+ return result;
+ }
+
+ makeTickAll() {
+ if (this.tickAll) {
+ const newTicked = [];
+ for (const key of Object.keys(this.links)) {
+ if (key.indexOf('r-') != 0)
+ newTicked.push(key);
+ }
+ this.ticked = newTicked;
+ } else {
+ this.ticked = [];
+ }
+ }
+
+ checkAllTicked() {
+ const ticked = new Set(this.ticked);
+
+ let newTickAll = !!(this.nodes.length);
+ for (const key of Object.keys(this.links)) {
+ if (key.indexOf('r-') != 0 && !ticked.has(key))
+ newTickAll = false;
+ }
+ this.tickAll = newTickAll;
+ }
+
+ resetSearch() {
+ this.search = '';
+ this.$refs.search.focus();
+ }
+
+ openSelected() {
+ if (!this.selected)
+ return;
+ if (this.selected.indexOf('r-') === 0) {//rootLink
+ this.$emit('do-action', {action: 'setRootLink', data: this.links[this.selected].l});
+ } else {//selectedLink
+ this.$emit('do-action', {action: 'setSelectedLink', data: this.links[this.selected].l});
+ }
+ this.close();
+ }
+
+ editBookmark() {
+ this.$emit('do-action', {action: 'editBookmark', data: {link: this.links[this.selected].l, desc: this.links[this.selected].c}});
+ }
+
+ addBookmark() {
+ this.$emit('do-action', {action: 'addBookmark'});
+ }
+
+ async delBookmark() {
+ const newLibs = _.cloneDeep(this.libs);
+
+ if (await this.$root.stdDialog.confirm(`Подтвердите удаление ${this.ticked.length} закладок:`, ' ')) {
+ const ticked = new Set(this.ticked);
+ for (let i = newLibs.groups.length - 1; i >= 0; i--) {
+ const g = newLibs.groups[i];
+ for (let j = g.list.length - 1; j >= 0; j--) {
+ if (ticked.has(g.list[j].l)) {
+ delete g.list[j];
+ }
+ }
+ g.list = g.list.filter(v => v);
+ if (!g.list.length)
+ delete newLibs.groups[i];
+ else {
+ const item = lu.getListItemByLink(g.list, g.s);
+ if (!item)
+ g.s = g.list[0].l;
+ }
+ }
+
+ newLibs.groups = newLibs.groups.filter(v => v);
+ this.ticked = [];
+ this.selected = '';
+ this.$emit('do-action', {action: 'setLibs', data: newLibs});
+ }
+ }
+
+ moveBookmark(down = false) {
+ const newLibs = _.cloneDeep(this.libs);
+
+ const ticked = new Set(this.ticked);
+ let moved = false;
+ let prevFull = false;
+ if (!down) {
+ for (let i = 0; i < newLibs.groups.length; i++) {
+ const g = newLibs.groups[i];
+ let count = 0;
+ for (let j = 0; j < g.list.length; j++) {
+ if (ticked.has(g.list[j].l)) {
+ if (j > 0 && !ticked.has(g.list[j - 1].l)) {
+ [g.list[j], g.list[j - 1]] = [g.list[j - 1], g.list[j]];
+ moved = true;
+ }
+ count++;
+ }
+ }
+
+ if (count == g.list.length && !prevFull && i > 0) {
+ const gs = newLibs.groups;
+ [gs[i], gs[i - 1]] = [gs[i - 1], gs[i]];
+ moved = true;
+ } else
+ prevFull = (count == g.list.length);
+ }
+ } else {
+ for (let i = newLibs.groups.length - 1; i >= 0; i--) {
+ const g = newLibs.groups[i];
+ let count = 0;
+ for (let j = g.list.length - 1; j >= 0; j--) {
+ if (ticked.has(g.list[j].l)) {
+ if (j < g.list.length - 1 && !ticked.has(g.list[j + 1].l)) {
+ [g.list[j], g.list[j + 1]] = [g.list[j + 1], g.list[j]];
+ moved = true;
+ }
+ count++;
+ }
+ }
+
+ if (count == g.list.length && !prevFull && i < newLibs.groups.length - 1) {
+ const gs = newLibs.groups;
+ [gs[i], gs[i + 1]] = [gs[i + 1], gs[i]];
+ moved = true;
+ } else
+ prevFull = (count == g.list.length);
+ }
+ }
+
+ if (moved)
+ this.$emit('do-action', {action: 'setLibs', data: newLibs});
+ }
+
+ async setDefaultBookmarks() {
+ const result = await this.$root.stdDialog.prompt(`Введите 'да' для сброса всех закладок в предустановленные значения:`, ' ', {
+ inputValidator: (str) => { if (str && str.toLowerCase() === 'да') return true; else return 'Удаление не подтверждено'; },
+ });
+
+ if (result && result.value && result.value.toLowerCase() == 'да') {
+ this.$emit('do-action', {action: 'setLibs', data: _.cloneDeep(rstore.libsDefaults)});
+ }
+ }
+
close() {
+ this.afterInit = false;
this.$emit('close');
}
keyHook(event) {
+ if (this.addBookmarkVisible)
+ return false;
+
if (event.type == 'keydown' && event.key == 'Escape') {
this.close();
return true;
@@ -56,4 +310,38 @@ class BookmarkSettings extends BookmarkSettingsProps {
diff --git a/client/components/ExternalLibs/ExternalLibs.vue b/client/components/ExternalLibs/ExternalLibs.vue
index 19611658..d5f243e8 100644
--- a/client/components/ExternalLibs/ExternalLibs.vue
+++ b/client/components/ExternalLibs/ExternalLibs.vue
@@ -5,6 +5,10 @@
+
+
+ Справка
+
На весь экран
@@ -23,12 +27,12 @@
Добавить закладку
-
+
Настроить закладки
- {{ removeProtocol(rootLink) }}
+ {{ rootLinkWithoutProtocol }}
@@ -44,16 +48,21 @@
@focus="selectAllOnFocus" @keydown="bookUrlKeyDown"
>
-
+
Вернуться на стартовую страницу
-
+
Загрузить URL во фрейм
+
+
+ Опции
+
+
- Открыть
+ Открыть
Открыть в читалке
@@ -68,7 +77,8 @@
- Добавить закладку
+
Редактировать закладку
+
Добавить закладку
@@ -96,8 +106,30 @@
OK
+
+
-
+
+
+
@@ -113,6 +145,7 @@ import BookmarkSettings from './BookmarkSettings/BookmarkSettings.vue';
import rstore from '../../store/modules/reader';
import * as utils from '../../share/utils';
+import * as lu from './linkUtils';
const proxySubst = {
'http://flibusta.is': 'http://b.liberama.top:23480',
@@ -128,44 +161,75 @@ export default @Component({
libs: function() {
this.loadLibs();
},
- rootLink: function() {
- this.updateSelectedLink();
- this.updateStartLink();
- },
- selectedLink: function() {
- this.updateStartLink();
- },
defaultRootLink: function() {
this.updateBookmarkLink();
- }
+ },
+ bookUrl: function(newValue) {
+ const value = lu.addProtocol(newValue);
+ const subst = this.makeProxySubst(value, true);
+ if (value != subst) {
+ this.$nextTick(() => {
+ this.bookUrl = subst;
+ });
+ }
+ },
+ bookmarkLink: function(newValue) {
+ const value = lu.addProtocol(newValue);
+ const subst = this.makeProxySubst(value, true);
+ if (value != subst) {
+ this.$nextTick(() => {
+ this.bookmarkLink = subst;
+ });
+ }
+ },
+ closeAfterSubmit: function(newValue) {
+ this.commitProp('closeAfterSubmit', newValue);
+ },
+ openInFrameOnEnter: function(newValue) {
+ this.commitProp('openInFrameOnEnter', newValue);
+ },
+ openInFrameOnAdd: function(newValue) {
+ this.commitProp('openInFrameOnAdd', newValue);
+ },
}
})
class ExternalLibs extends Vue {
ready = false;
frameVisible = false;
- startLink = '';
rootLink = '';
selectedLink = '';
frameSrc = '';
bookUrl = '';
libs = {};
fullScreenActive = false;
- addBookmarkVisible = false;
transparentLayoutVisible = false;
+ addBookmarkVisible = false;
+ optionsVisible = false;
+
+ addBookmarkMode = '';
bookmarkLink = '';
bookmarkDesc = '';
defaultRootLink = '';
bookmarkSettingsActive = false;
+ closeAfterSubmit = false;
+ openInFrameOnEnter = false;
+ openInFrameOnAdd = false;
+
created() {
+ this.oldStartLink = '';
+ this.justOpened = true;
this.$root.addKeyHook(this.keyHook);
document.addEventListener('fullscreenchange', () => {
this.fullScreenActive = (document.fullscreenElement !== null);
});
+ this.debouncedGoToLink = _.debounce((link) => {
+ this.goToLink(link);
+ }, 100, {'maxWait':200});
//this.commit = this.$store.commit;
//this.commit('reader/setLibs', rstore.libsDefaults);
}
@@ -258,8 +322,6 @@ class ExternalLibs extends Vue {
} else if (d.type == 'libs') {
this.ready = true;
this.libs = _.cloneDeep(d.data);
- if (!this.frameSrc)
- this.goToLink(this.libs.startLink);
} else if (d.type == 'notify') {
this.$root.notify.success(d.data, '', {position: 'bottom-right'});
}
@@ -284,11 +346,31 @@ class ExternalLibs extends Vue {
this.sendMessage({type: 'libs', data: libs});
}
+ commitProp(prop, value) {
+ let libs = _.cloneDeep(this.libs);
+ libs[prop] = value;
+ this.commitLibs(libs);
+ }
+
loadLibs() {
const libs = this.libs;
- this.startLink = (libs.comment ? libs.comment + ' ': '') + this.removeProtocol(libs.startLink);
- this.rootLink = this.getOrigin(libs.startLink);
- this.updateSelectedLink();
+
+ this.selectedLink = libs.startLink;
+ this.closeAfterSubmit = libs.closeAfterSubmit || false;
+ this.openInFrameOnEnter = libs.openInFrameOnEnter || false;
+ this.openInFrameOnAdd = libs.openInFrameOnAdd || false;
+
+ this.updateStartLink();
+ }
+
+ doAction(event) {
+ switch (event.action) {
+ case 'setLibs': this.commitLibs(event.data); break;
+ case 'setRootLink': this.rootLink = event.data; this.rootLinkInput(); break;
+ case 'setSelectedLink': this.selectedLink = event.data; this.selectedLinkInput(); break;
+ case 'editBookmark': this.addBookmark('edit', event.data.link, event.data.desc); break;
+ case 'addBookmark': this.addBookmark('add'); break;
+ }
}
get mode() {
@@ -297,32 +379,60 @@ class ExternalLibs extends Vue {
get header() {
let result = (this.ready ? 'Библиотека' : 'Загрузка...');
- if (this.ready && this.startLink) {
- result += ` | ${this.startLink}`;
+ if (this.ready && this.selectedLink) {
+ result += ` | ${(this.libs.comment ? this.libs.comment + ' ': '') + lu.removeProtocol(this.libs.startLink)}`;
}
this.$root.$emit('set-app-title', result);
return result;
}
- updateSelectedLink() {
- if (!this.ready)
- return;
- const index = this.getRootIndexByUrl(this.libs.groups, this.rootLink);
- if (index >= 0)
- this.selectedLink = this.libs.groups[index].s;
+ get rootLinkWithoutProtocol() {
+ return lu.removeProtocol(this.rootLink);
}
- updateStartLink() {
+ updateSelectedLinkByRoot() {
if (!this.ready)
return;
- const index = this.getRootIndexByUrl(this.libs.groups, this.rootLink);
+
+ const index = lu.getSafeRootIndexByUrl(this.libs.groups, this.rootLink);
+ if (index >= 0)
+ this.selectedLink = this.libs.groups[index].s;
+ else
+ this.selectedLink = '';
+ }
+
+ updateStartLink(force) {
+ if (!this.ready)
+ return;
+
+ let index = -1;
+ try {
+ this.rootLink = lu.getOrigin(this.selectedLink);
+ index = lu.getRootIndexByUrl(this.libs.groups, this.rootLink);
+ } catch(e) {
+ //
+ }
+
if (index >= 0) {
let libs = _.cloneDeep(this.libs);
- libs.groups[index].s = this.selectedLink;
- libs.startLink = this.selectedLink;
- libs.comment = this.getCommentByLink(libs.groups[index].list, this.selectedLink);
- this.goToLink(this.selectedLink);
- this.commitLibs(libs);
+ const com = this.getCommentByLink(libs.groups[index].list, this.selectedLink);
+ if (libs.groups[index].s != this.selectedLink ||
+ libs.startLink != this.selectedLink ||
+ libs.comment != com) {
+ libs.groups[index].s = this.selectedLink;
+ libs.startLink = this.selectedLink;
+ libs.comment = com;
+ this.commitLibs(libs);
+ }
+
+ if (force || this.oldStartLink != libs.startLink) {
+ this.oldStartLink = libs.startLink;
+ this.debouncedGoToLink(this.selectedLink);
+ }
+ } else {
+ this.rootLink = '';
+ this.selectedLink = '';
+ this.debouncedGoToLink(this.selectedLink);
}
}
@@ -332,7 +442,7 @@ class ExternalLibs extends Vue {
return result;
this.libs.groups.forEach(group => {
- result.push({label: this.removeProtocol(group.r), value: group.r});
+ result.push({label: lu.removeProtocol(group.r), value: group.r});
});
return result;
@@ -342,7 +452,7 @@ class ExternalLibs extends Vue {
let result = [];
rstore.libsDefaults.groups.forEach(group => {
- result.push({label: this.removeProtocol(group.r), value: group.r});
+ result.push({label: lu.removeProtocol(group.r), value: group.r});
});
return result;
@@ -353,10 +463,10 @@ class ExternalLibs extends Vue {
if (!this.ready)
return result;
- const index = this.getRootIndexByUrl(this.libs.groups, this.rootLink);
+ const index = lu.getSafeRootIndexByUrl(this.libs.groups, this.rootLink);
if (index >= 0) {
this.libs.groups[index].list.forEach(link => {
- result.push({label: (link.c ? link.c + ' ': '') + this.removeOrigin(link.l), value: link.l});
+ result.push({label: (link.c ? link.c + ' ': '') + lu.removeOrigin(link.l), value: link.l});
});
}
@@ -365,66 +475,33 @@ class ExternalLibs extends Vue {
openBookUrlInFrame() {
if (this.bookUrl) {
- this.goToLink(this.addProtocol(this.bookUrl));
+ this.goToLink(lu.addProtocol(this.bookUrl));
}
}
goToLink(link) {
- if (!this.ready)
+ if (!this.ready || !link)
return;
+ if (!link) {
+ this.frameVisible = false;
+ return;
+ }
+
this.frameSrc = this.makeProxySubst(link);
+
this.frameVisible = false;
this.$nextTick(() => {
this.frameVisible = true;
this.$nextTick(() => {
- this.$refs.frame.contentWindow.focus();
+ if (this.$refs.frame)
+ this.$refs.frame.contentWindow.focus();
});
});
}
- addProtocol(url) {
- if ((url.indexOf('http://') != 0) && (url.indexOf('https://') != 0))
- return 'http://' + url;
- return url;
- }
-
- removeProtocol(url) {
- return url.replace(/(^\w+:|^)\/\//, '');
- }
-
- getOrigin(url) {
- const parsed = new URL(url);
- return parsed.origin;
- }
-
- removeOrigin(url) {
- const parsed = new URL(url);
- const result = url.substring(parsed.origin.length);
- return (result ? result : '/');
- }
-
- getRootIndexByUrl(groups, url) {
- if (!this.ready)
- return -1;
- const origin = this.getOrigin(url);
- for (let i = 0; i < groups.length; i++) {
- if (groups[i].r == origin)
- return i;
- }
- return -1;
- }
-
- getListItemByLink(list, link) {
- for (const item of list) {
- if (item.l == link)
- return item;
- }
- return null;
- }
-
getCommentByLink(list, link) {
- const item = this.getListItemByLink(list, link);
+ const item = lu.getListItemByLink(list, link);
return (item ? item.c : '');
}
@@ -432,7 +509,7 @@ class ExternalLibs extends Vue {
for (const [key, value] of Object.entries(proxySubst)) {
if (reverse && value == url.substring(0, value.length)) {
return key + url.substring(value.length);
- } else if (key == url.substring(0, key.length)) {
+ } else if (!reverse && key == url.substring(0, key.length)) {
return value + url.substring(key.length);
}
}
@@ -446,29 +523,37 @@ class ExternalLibs extends Vue {
}
rootLinkInput() {
- this.updateSelectedLink();
- this.updateStartLink();
+ this.updateSelectedLinkByRoot();
+ this.updateStartLink(true);
}
selectedLinkInput() {
- this.updateStartLink();
+ this.updateStartLink(true);
}
submitUrl() {
if (this.bookUrl) {
this.sendMessage({type: 'submitUrl', data: {
- url: this.makeProxySubst(this.addProtocol(this.bookUrl), true),
+ url: this.bookUrl,
force: true
}});
this.bookUrl = '';
- if (this.libs.closeAfterSubmit)
+ if (this.closeAfterSubmit)
this.close();
}
}
- addBookmark() {
- this.bookmarkLink = (this.bookUrl ? this.makeProxySubst(this.addProtocol(this.bookUrl), true) : '');
- this.bookmarkDesc = '';
+ addBookmark(mode = 'add', link = '', desc = '') {
+
+ if (mode == 'edit') {
+ this.editBookmarkLink = this.bookmarkLink = link;
+ this.editBookmarkDesc = this.bookmarkDesc = desc;
+ } else {
+ this.bookmarkLink = this.bookUrl;
+ this.bookmarkDesc = '';
+ }
+
+ this.addBookmarkMode = mode;
this.addBookmarkVisible = true;
this.$nextTick(() => {
this.$refs.bookmarkLink.focus();
@@ -477,7 +562,7 @@ class ExternalLibs extends Vue {
}
updateBookmarkLink() {
- const index = this.getRootIndexByUrl(rstore.libsDefaults.groups, this.defaultRootLink);
+ const index = lu.getSafeRootIndexByUrl(rstore.libsDefaults.groups, this.defaultRootLink);
if (index >= 0) {
this.bookmarkLink = rstore.libsDefaults.groups[index].s;
this.bookmarkDesc = this.getCommentByLink(rstore.libsDefaults.groups[index].list, this.bookmarkLink);
@@ -500,8 +585,9 @@ class ExternalLibs extends Vue {
bookmarkDescKeyDown(event) {
if (event.key == 'Enter') {
- this.okAddBookmark();
+ event.stopPropagation();
event.preventDefault();
+ this.okAddBookmark();
}
}
@@ -509,46 +595,76 @@ class ExternalLibs extends Vue {
if (!this.bookmarkLink)
return;
- const link = this.addProtocol(this.bookmarkLink);
+ const link = (this.addBookmarkMode == 'edit' ? lu.addProtocol(this.editBookmarkLink) : lu.addProtocol(this.bookmarkLink));
let index = -1;
try {
- index = this.getRootIndexByUrl(this.libs.groups, link);
+ index = lu.getRootIndexByUrl(this.libs.groups, link);
} catch (e) {
await this.$root.stdDialog.alert('Неверный формат ссылки', 'Ошибка');
return;
}
+ let libs = _.cloneDeep(this.libs);
+
+ //добавление
//есть группа в закладках
if (index >= 0) {
- const item = this.getListItemByLink(this.libs.groups[index].list, link);
-
- if (!item || item.c != this.bookmarkDesc) {
- //добавляем
- let libs = _.cloneDeep(this.libs);
+ const item = lu.getListItemByLink(libs.groups[index].list, link);
+ //редактирование
+ if (item && this.addBookmarkMode == 'edit') {
+ if (item) {
+ //редактируем
+ item.l = link;
+ item.c = this.bookmarkDesc;
+ this.commitLibs(libs);
+ } else {
+ await this.$root.stdDialog.alert('Не удалось отредактировать закладку', 'Ошибка');
+ }
+ } else if (!item) {
+ //добавляем
if (libs.groups[index].list.length >= 100) {
await this.$root.stdDialog.alert('Достигнут предел количества закладок для этого сайта', 'Ошибка');
return;
}
libs.groups[index].list.push({l: link, c: this.bookmarkDesc});
+
+ if (this.openInFrameOnAdd) {
+ libs.startLink = link;
+ libs.comment = this.bookmarkDesc;
+ }
+
this.commitLibs(libs);
+ } else if (item.c != this.bookmarkDesc) {
+ if (await this.$root.stdDialog.confirm(`Такая закладка уже существует с другим описанием.
` +
+ `Заменить '${this.$sanitize(item.c)}' на '${this.$sanitize(this.bookmarkDesc)}'?`, ' ')) {
+ item.c = this.bookmarkDesc;
+ this.commitLibs(libs);
+ } else
+ return;
+ } else {
+ await this.$root.stdDialog.alert('Такая закладка уже существует', ' ');
+ return;
}
} else {//нет группы в закладках
- let libs = _.cloneDeep(this.libs);
-
if (libs.groups.length >= 100) {
await this.$root.stdDialog.alert('Достигнут предел количества различных сайтов в закладках', 'Ошибка');
return;
}
//добавляем сначала группу
- libs.groups.push({r: this.getOrigin(link), s: link, list: []});
+ libs.groups.push({r: lu.getOrigin(link), s: link, list: []});
- index = this.getRootIndexByUrl(libs.groups, link);
+ index = lu.getSafeRootIndexByUrl(libs.groups, link);
if (index >= 0)
libs.groups[index].list.push({l: link, c: this.bookmarkDesc});
+ if (this.openInFrameOnAdd) {
+ libs.startLink = link;
+ libs.comment = this.bookmarkDesc;
+ }
+
this.commitLibs(libs);
}
@@ -582,7 +698,12 @@ class ExternalLibs extends Vue {
bookUrlKeyDown(event) {
if (event.key == 'Enter') {
- this.submitUrl();
+ if (!this.openInFrameOnEnter) {
+ this.submitUrl();
+ } else {
+ if (this.bookUrl)
+ this.goToLink(this.bookUrl);
+ }
event.preventDefault();
}
}
@@ -598,12 +719,34 @@ class ExternalLibs extends Vue {
this.bookmarkSettingsActive = false;
}
+ showHelp() {
+ this.$root.stdDialog.alert(`
+Окно 'Библиотека' позволяет открывать ссылки в читалке без переключения между окнами,
+что особенно актуально для мобильных устройств.
+
+'Библиотека' разрешает свободный доступ к сайту flibusta.is. Имеется возможность управлять закладками
+на понравившиеся ресурсы, книги или страницы авторов. Открытие ссылок и навигация осуществляется во фрейме, но,
+к сожалению, в нем открываются не все страницы.
+
+
+Из-за проблем с безопасностью, навигация 'вперед-назад' во фрейме осуществляется с помощью контекстного меню правой кнопкой мыши.
+На мобильных устройствах для этого служит системная клавиша 'Назад (стрелка влево)' и опция 'Вперед (стрелка вправо)' в меню браузера.
+
+
+Приятного пользования ;-)
+
+ `, 'Справка', {iconName: 'la la-info-circle'});
+ }
+
keyHook(event) {
if (this.$root.rootRoute() == '/external-libs') {
+ if (this.$root.stdDialog.active)
+ return false;
+
if (this.bookmarkSettingsActive && this.$refs.bookmarkSettings.keyHook(event))
return true;
- if (this.$refs.dialogAddBookmark.active)
+ if (this.addBookmarkVisible || this.optionsVisible)
return false;
if (event.type == 'keydown' && event.key == 'F4') {
@@ -621,7 +764,6 @@ class ExternalLibs extends Vue {
}
return false;
}
-
}
//-----------------------------------------------------------------------------
diff --git a/client/components/ExternalLibs/linkUtils.js b/client/components/ExternalLibs/linkUtils.js
new file mode 100644
index 00000000..395c9028
--- /dev/null
+++ b/client/components/ExternalLibs/linkUtils.js
@@ -0,0 +1,48 @@
+export function addProtocol(url) {
+ if ((url.indexOf('http://') != 0) && (url.indexOf('https://') != 0))
+ return 'http://' + url;
+ return url;
+}
+
+export function removeProtocol(url) {
+ return url.replace(/(^\w+:|^)\/\//, '');
+}
+
+export function getOrigin(url) {
+ const parsed = new URL(url);
+ return parsed.origin;
+}
+
+export function removeOrigin(url) {
+ const parsed = new URL(url);
+ const result = url.substring(parsed.origin.length);
+ return (result ? result : '/');
+}
+
+export function getRootIndexByUrl(groups, url) {
+ const origin = getOrigin(url);
+ for (let i = 0; i < groups.length; i++) {
+ if (groups[i].r == origin)
+ return i;
+ }
+ return -1;
+}
+
+export function getSafeRootIndexByUrl(groups, url) {
+ let index = -1;
+ try {
+ index = getRootIndexByUrl(groups, url);
+ } catch(e) {
+ //
+ }
+ return index;
+}
+
+export function getListItemByLink(list, link) {
+ for (const item of list) {
+ if (item.l == link)
+ return item;
+ }
+ return null;
+}
+
diff --git a/client/components/Reader/SettingsPage/SettingsPage.vue b/client/components/Reader/SettingsPage/SettingsPage.vue
index c926be16..404e3ade 100644
--- a/client/components/Reader/SettingsPage/SettingsPage.vue
+++ b/client/components/Reader/SettingsPage/SettingsPage.vue
@@ -407,7 +407,7 @@ class SettingsPage extends Vue {
return;
try {
- const result = await this.$root.stdDialog.prompt(`Предупреждение! Удаление профиля '${this.currentProfile}' необратимо.` +
+ const result = await this.$root.stdDialog.prompt(`Предупреждение! Удаление профиля '${this.$sanitize(this.currentProfile)}' необратимо.` +
`
Все настройки профиля будут потеряны, однако список читаемых книг сохранится.` +
`
Введите 'да' для подтверждения удаления:`, ' ', {
inputValidator: (str) => { if (str && str.toLowerCase() === 'да') return true; else return 'Удаление не подтверждено'; },
diff --git a/client/components/Reader/SettingsPage/include/ProfilesTab.inc b/client/components/Reader/SettingsPage/include/ProfilesTab.inc
index b9e0a898..ad94f74a 100644
--- a/client/components/Reader/SettingsPage/include/ProfilesTab.inc
+++ b/client/components/Reader/SettingsPage/include/ProfilesTab.inc
@@ -22,7 +22,7 @@
diff --git a/client/components/share/StdDialog.vue b/client/components/share/StdDialog.vue
index c10d5970..4c673d36 100644
--- a/client/components/share/StdDialog.vue
+++ b/client/components/share/StdDialog.vue
@@ -6,7 +6,7 @@