mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 09:58:32 +02:00
start of mobile layout
This commit is contained in:
parent
f64b343d8d
commit
4f744564f2
@ -1,151 +1,22 @@
|
|||||||
import treeService from "./services/tree.js";
|
import glob from './services/glob.js';
|
||||||
import treeCache from "./services/tree_cache.js";
|
import macInit from './services/mac_init.js';
|
||||||
import treeBuilder from "./services/tree_builder.js";
|
import options from "./services/options.js";
|
||||||
import contextMenuWidget from "./services/context_menu.js";
|
import noteContentRenderer from "./services/note_content_renderer.js";
|
||||||
import branchService from "./services/branches.js";
|
|
||||||
import utils from "./services/utils.js";
|
|
||||||
import appContext from "./services/app_context.js";
|
import appContext from "./services/app_context.js";
|
||||||
import noteCreateService from "./services/note_create.js";
|
import FlexContainer from "./widgets/flex_container.js";
|
||||||
import glob from "./services/glob.js";
|
import EmptyTypeWidget from "./widgets/type_widgets/empty.js";
|
||||||
|
import TextTypeWidget from "./widgets/type_widgets/text.js";
|
||||||
|
import CodeTypeWidget from "./widgets/type_widgets/code.js";
|
||||||
|
import FileTypeWidget from "./widgets/type_widgets/file.js";
|
||||||
|
import ImageTypeWidget from "./widgets/type_widgets/image.js";
|
||||||
|
import SearchTypeWidget from "./widgets/type_widgets/search.js";
|
||||||
|
import RenderTypeWidget from "./widgets/type_widgets/render.js";
|
||||||
|
import RelationMapTypeWidget from "./widgets/type_widgets/relation_map.js";
|
||||||
|
import ProtectedSessionTypeWidget from "./widgets/type_widgets/protected_session.js";
|
||||||
|
import BookTypeWidget from "./widgets/type_widgets/book.js";
|
||||||
|
import MobileLayout from "./widgets/mobile_layout.js";
|
||||||
|
|
||||||
const $leftPane = $("#left-pane");
|
macInit.init();
|
||||||
const $tree = $("#tree");
|
|
||||||
const $detail = $("#detail");
|
|
||||||
|
|
||||||
function togglePanes() {
|
appContext.setLayout(new MobileLayout());
|
||||||
if (!$leftPane.is(":visible") || !$detail.is(":visible")) {
|
appContext.start();
|
||||||
$detail.toggleClass("d-none");
|
|
||||||
$leftPane.toggleClass("d-none");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showDetailPane() {
|
|
||||||
if (!$detail.is(":visible")) {
|
|
||||||
$detail.removeClass("d-none");
|
|
||||||
$leftPane.addClass("d-none");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$detail.on("click", ".close-detail-button",() => {
|
|
||||||
// no page is opened
|
|
||||||
document.location.hash = '-';
|
|
||||||
|
|
||||||
togglePanes();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function showTree() {
|
|
||||||
const treeData = await treeBuilder.prepareTree();
|
|
||||||
|
|
||||||
$tree.fancytree({
|
|
||||||
autoScroll: true,
|
|
||||||
extensions: ["dnd5", "clones"],
|
|
||||||
source: treeData,
|
|
||||||
scrollParent: $tree,
|
|
||||||
minExpandLevel: 2, // root can't be collapsed
|
|
||||||
click: (event, data) => {
|
|
||||||
if (data.targetType !== 'expander' && data.node.isActive()) {
|
|
||||||
// this is important for single column mobile view, otherwise it's not possible to see again previously displayed note
|
|
||||||
$tree.fancytree('getTree').reactivate(true);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
activate: async (event, data) => {
|
|
||||||
const node = data.node;
|
|
||||||
|
|
||||||
treeService.clearSelectedNodes();
|
|
||||||
|
|
||||||
showDetailPane();
|
|
||||||
|
|
||||||
const notePath = await treeService.getNotePath(node);
|
|
||||||
|
|
||||||
},
|
|
||||||
expand: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, true),
|
|
||||||
collapse: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, false),
|
|
||||||
init: (event, data) => treeService.treeInitialized(), // don't collapse to short form
|
|
||||||
dnd5: dragAndDropSetup,
|
|
||||||
lazyLoad: function(event, data) {
|
|
||||||
const noteId = data.node.data.noteId;
|
|
||||||
|
|
||||||
data.result = treeCache.getNote(noteId).then(note => treeBuilder.prepareBranch(note));
|
|
||||||
},
|
|
||||||
clones: {
|
|
||||||
highlightActiveClones: true
|
|
||||||
},
|
|
||||||
// this is done to automatically lazy load all expanded search notes after tree load
|
|
||||||
loadChildren: (event, data) => {
|
|
||||||
data.node.visit((subNode) => {
|
|
||||||
// Load all lazy/unloaded child nodes
|
|
||||||
// (which will trigger `loadChildren` recursively)
|
|
||||||
if (subNode.isUndefined() && subNode.isExpanded()) {
|
|
||||||
subNode.load();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
treeService.setTree($.ui.fancytree.getTree("#tree"));
|
|
||||||
}
|
|
||||||
|
|
||||||
$detail.on("click", ".note-menu-button", async e => {
|
|
||||||
// FIXME
|
|
||||||
const node = appContext.getMainNoteTree().getActiveNode();
|
|
||||||
const branch = treeCache.getBranch(node.data.branchId);
|
|
||||||
const note = await treeCache.getNote(node.data.noteId);
|
|
||||||
const parentNote = await treeCache.getNote(branch.parentNoteId);
|
|
||||||
const isNotRoot = note.noteId !== 'root';
|
|
||||||
|
|
||||||
const items = [
|
|
||||||
{ title: "Insert note after", cmd: "insertNoteAfter", uiIcon: "plus",
|
|
||||||
enabled: isNotRoot && parentNote.type !== 'search' },
|
|
||||||
{ title: "Insert child note", cmd: "insertChildNote", uiIcon: "plus",
|
|
||||||
enabled: note.type !== 'search' },
|
|
||||||
{ title: "Delete this note", cmd: "delete", uiIcon: "trash",
|
|
||||||
enabled: isNotRoot && parentNote.type !== 'search' }
|
|
||||||
];
|
|
||||||
|
|
||||||
contextMenuWidget.initContextMenu(e, {
|
|
||||||
getContextMenuItems: () => items,
|
|
||||||
selectContextMenuItem: async (event, cmd) => {
|
|
||||||
if (cmd === "insertNoteAfter") {
|
|
||||||
const parentNoteId = node.data.parentNoteId;
|
|
||||||
const isProtected = await treeService.getParentProtectedStatus(node);
|
|
||||||
|
|
||||||
noteCreateService.createNote(parentNoteId, {
|
|
||||||
isProtected: isProtected,
|
|
||||||
target: 'after',
|
|
||||||
targetBranchId: node.data.branchId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (cmd === "insertChildNote") {
|
|
||||||
noteCreateService.createNote(node.data.noteId);
|
|
||||||
}
|
|
||||||
else if (cmd === "delete") {
|
|
||||||
if (await branchService.deleteNotes([node])) {
|
|
||||||
// move to the tree
|
|
||||||
togglePanes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error("Unrecognized command " + cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#switch-to-desktop-button").on('click', () => {
|
|
||||||
utils.setCookie('trilium-device', 'desktop');
|
|
||||||
|
|
||||||
utils.reloadApp();
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#log-out-button").on('click', () => {
|
|
||||||
$("#logout-form").trigger('submit');
|
|
||||||
});
|
|
||||||
|
|
||||||
// this is done so that startNotePath is not used
|
|
||||||
if (!document.location.hash) {
|
|
||||||
document.location.hash = '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
showTree();
|
|
151
src/public/javascripts/mobile_old.js
Normal file
151
src/public/javascripts/mobile_old.js
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import treeService from "./services/tree.js";
|
||||||
|
import treeCache from "./services/tree_cache.js";
|
||||||
|
import treeBuilder from "./services/tree_builder.js";
|
||||||
|
import contextMenuWidget from "./services/context_menu.js";
|
||||||
|
import branchService from "./services/branches.js";
|
||||||
|
import utils from "./services/utils.js";
|
||||||
|
import appContext from "./services/app_context.js";
|
||||||
|
import noteCreateService from "./services/note_create.js";
|
||||||
|
import glob from "./services/glob.js";
|
||||||
|
|
||||||
|
const $leftPane = $("#left-pane");
|
||||||
|
const $tree = $("#tree");
|
||||||
|
const $detail = $("#detail");
|
||||||
|
|
||||||
|
function togglePanes() {
|
||||||
|
if (!$leftPane.is(":visible") || !$detail.is(":visible")) {
|
||||||
|
$detail.toggleClass("d-none");
|
||||||
|
$leftPane.toggleClass("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDetailPane() {
|
||||||
|
if (!$detail.is(":visible")) {
|
||||||
|
$detail.removeClass("d-none");
|
||||||
|
$leftPane.addClass("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$detail.on("click", ".close-detail-button",() => {
|
||||||
|
// no page is opened
|
||||||
|
document.location.hash = '-';
|
||||||
|
|
||||||
|
togglePanes();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function showTree() {
|
||||||
|
const treeData = await treeBuilder.prepareTree();
|
||||||
|
|
||||||
|
$tree.fancytree({
|
||||||
|
autoScroll: true,
|
||||||
|
extensions: ["dnd5", "clones"],
|
||||||
|
source: treeData,
|
||||||
|
scrollParent: $tree,
|
||||||
|
minExpandLevel: 2, // root can't be collapsed
|
||||||
|
click: (event, data) => {
|
||||||
|
if (data.targetType !== 'expander' && data.node.isActive()) {
|
||||||
|
// this is important for single column mobile view, otherwise it's not possible to see again previously displayed note
|
||||||
|
$tree.fancytree('getTree').reactivate(true);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
activate: async (event, data) => {
|
||||||
|
const node = data.node;
|
||||||
|
|
||||||
|
treeService.clearSelectedNodes();
|
||||||
|
|
||||||
|
showDetailPane();
|
||||||
|
|
||||||
|
const notePath = await treeService.getNotePath(node);
|
||||||
|
|
||||||
|
},
|
||||||
|
expand: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, true),
|
||||||
|
collapse: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, false),
|
||||||
|
init: (event, data) => treeService.treeInitialized(), // don't collapse to short form
|
||||||
|
dnd5: dragAndDropSetup,
|
||||||
|
lazyLoad: function(event, data) {
|
||||||
|
const noteId = data.node.data.noteId;
|
||||||
|
|
||||||
|
data.result = treeCache.getNote(noteId).then(note => treeBuilder.prepareBranch(note));
|
||||||
|
},
|
||||||
|
clones: {
|
||||||
|
highlightActiveClones: true
|
||||||
|
},
|
||||||
|
// this is done to automatically lazy load all expanded search notes after tree load
|
||||||
|
loadChildren: (event, data) => {
|
||||||
|
data.node.visit((subNode) => {
|
||||||
|
// Load all lazy/unloaded child nodes
|
||||||
|
// (which will trigger `loadChildren` recursively)
|
||||||
|
if (subNode.isUndefined() && subNode.isExpanded()) {
|
||||||
|
subNode.load();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
treeService.setTree($.ui.fancytree.getTree("#tree"));
|
||||||
|
}
|
||||||
|
|
||||||
|
$detail.on("click", ".note-menu-button", async e => {
|
||||||
|
// FIXME
|
||||||
|
const node = appContext.getMainNoteTree().getActiveNode();
|
||||||
|
const branch = treeCache.getBranch(node.data.branchId);
|
||||||
|
const note = await treeCache.getNote(node.data.noteId);
|
||||||
|
const parentNote = await treeCache.getNote(branch.parentNoteId);
|
||||||
|
const isNotRoot = note.noteId !== 'root';
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{ title: "Insert note after", cmd: "insertNoteAfter", uiIcon: "plus",
|
||||||
|
enabled: isNotRoot && parentNote.type !== 'search' },
|
||||||
|
{ title: "Insert child note", cmd: "insertChildNote", uiIcon: "plus",
|
||||||
|
enabled: note.type !== 'search' },
|
||||||
|
{ title: "Delete this note", cmd: "delete", uiIcon: "trash",
|
||||||
|
enabled: isNotRoot && parentNote.type !== 'search' }
|
||||||
|
];
|
||||||
|
|
||||||
|
contextMenuWidget.initContextMenu(e, {
|
||||||
|
getContextMenuItems: () => items,
|
||||||
|
selectContextMenuItem: async (event, cmd) => {
|
||||||
|
if (cmd === "insertNoteAfter") {
|
||||||
|
const parentNoteId = node.data.parentNoteId;
|
||||||
|
const isProtected = await treeService.getParentProtectedStatus(node);
|
||||||
|
|
||||||
|
noteCreateService.createNote(parentNoteId, {
|
||||||
|
isProtected: isProtected,
|
||||||
|
target: 'after',
|
||||||
|
targetBranchId: node.data.branchId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (cmd === "insertChildNote") {
|
||||||
|
noteCreateService.createNote(node.data.noteId);
|
||||||
|
}
|
||||||
|
else if (cmd === "delete") {
|
||||||
|
if (await branchService.deleteNotes([node])) {
|
||||||
|
// move to the tree
|
||||||
|
togglePanes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("Unrecognized command " + cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#switch-to-desktop-button").on('click', () => {
|
||||||
|
utils.setCookie('trilium-device', 'desktop');
|
||||||
|
|
||||||
|
utils.reloadApp();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#log-out-button").on('click', () => {
|
||||||
|
$("#logout-form").trigger('submit');
|
||||||
|
});
|
||||||
|
|
||||||
|
// this is done so that startNotePath is not used
|
||||||
|
if (!document.location.hash) {
|
||||||
|
document.location.hash = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
showTree();
|
@ -12,10 +12,12 @@ export default class Entrypoints extends Component {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// hot keys are active also inside inputs and content editables
|
if (jQuery.hotkeys) {
|
||||||
jQuery.hotkeys.options.filterInputAcceptingElements = false;
|
// hot keys are active also inside inputs and content editables
|
||||||
jQuery.hotkeys.options.filterContentEditable = false;
|
jQuery.hotkeys.options.filterInputAcceptingElements = false;
|
||||||
jQuery.hotkeys.options.filterTextInputs = false;
|
jQuery.hotkeys.options.filterContentEditable = false;
|
||||||
|
jQuery.hotkeys.options.filterTextInputs = false;
|
||||||
|
}
|
||||||
|
|
||||||
$(document).on('click', "a[data-action='note-revision']", async event => {
|
$(document).on('click', "a[data-action='note-revision']", async event => {
|
||||||
const linkEl = $(event.target);
|
const linkEl = $(event.target);
|
||||||
|
19
src/public/javascripts/widgets/mobile_layout.js
Normal file
19
src/public/javascripts/widgets/mobile_layout.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import FlexContainer from "./flex_container.js";
|
||||||
|
import NoteTitleWidget from "./note_title.js";
|
||||||
|
import NoteDetailWidget from "./note_detail.js";
|
||||||
|
import NoteTreeWidget from "./note_tree.js";
|
||||||
|
|
||||||
|
export default class MobileLayout {
|
||||||
|
getRootWidget(appContext) {
|
||||||
|
return new FlexContainer('row')
|
||||||
|
.setParent(appContext)
|
||||||
|
.id('root-widget')
|
||||||
|
.css('height', '100vh')
|
||||||
|
.child(new FlexContainer('column')
|
||||||
|
// .child(/* buttons */)
|
||||||
|
.child(new NoteTreeWidget()))
|
||||||
|
.child(new FlexContainer('column')
|
||||||
|
.child(new NoteTitleWidget())
|
||||||
|
.child(new NoteDetailWidget()));
|
||||||
|
}
|
||||||
|
}
|
@ -66,7 +66,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
|||||||
this.$widget.fancytree({
|
this.$widget.fancytree({
|
||||||
autoScroll: true,
|
autoScroll: true,
|
||||||
keyboard: false, // we takover keyboard handling in the hotkeys plugin
|
keyboard: false, // we takover keyboard handling in the hotkeys plugin
|
||||||
extensions: ["hotkeys", "dnd5", "clones"],
|
extensions: utils.isMobile() ? ["clones"] : ["hotkeys", "dnd5", "clones"],
|
||||||
source: treeData,
|
source: treeData,
|
||||||
scrollParent: this.$widget,
|
scrollParent: this.$widget,
|
||||||
minExpandLevel: 2, // root can't be collapsed
|
minExpandLevel: 2, // root can't be collapsed
|
||||||
|
@ -7,87 +7,12 @@
|
|||||||
<title>Trilium Notes</title>
|
<title>Trilium Notes</title>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="images/app-icons/ios/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="images/app-icons/ios/apple-touch-icon.png">
|
||||||
</head>
|
</head>
|
||||||
<body class="mobile">
|
<body class="mobile" style="display: none;">
|
||||||
<noscript>Trilium requires JavaScript to be enabled.</noscript>
|
<noscript>Trilium requires JavaScript to be enabled.</noscript>
|
||||||
|
|
||||||
<div id="toast-container" class="d-flex flex-column justify-content-center align-items-center"></div>
|
<div id="toast-container" class="d-flex flex-column justify-content-center align-items-center"></div>
|
||||||
|
|
||||||
<div class="row" id="container-row" style="display: none;">
|
|
||||||
|
|
||||||
<div id="left-pane" class="d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-5 col-md-4 col-lg-4 col-xl-4">
|
|
||||||
<div id="global-buttons">
|
|
||||||
<a id="create-top-level-note-button" title="Create new top level note" class="icon-action bx bx-folder-plus"></a>
|
|
||||||
|
|
||||||
<a id="collapse-tree-button" title="Collapse note tree" class="icon-action bx bx-layer-minus"></a>
|
|
||||||
|
|
||||||
<a id="scroll-to-active-note-button" title="Scroll to active note" class="icon-action bx bx-crosshair"></a>
|
|
||||||
|
|
||||||
<div class="dropdown">
|
|
||||||
<a id="global-actions-button" title="Global actions" class="icon-action bx bx-cog dropdown-toggle" data-toggle="dropdown"></a>
|
|
||||||
|
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
|
||||||
<a class="dropdown-item" id="switch-to-desktop-button"><span class="bx bx-laptop"></span> Switch to desktop version</a>
|
|
||||||
<a class="dropdown-item" id="log-out-button"><span class="bx bx-log-out"></span> Logout</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="tree"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="detail" class="d-none d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-7 col-md-8 col-lg-8">
|
|
||||||
<div class="note-tab-row">
|
|
||||||
<div class="note-tab-row-content"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="note-tab-container">
|
|
||||||
<div class="note-tab-content note-tab-content-template">
|
|
||||||
<div class="note-detail-content" style="width: 100%">
|
|
||||||
<div class="note-title-row">
|
|
||||||
<button type="button" class="note-menu-button action-button bx bx-menu"></button>
|
|
||||||
|
|
||||||
<input autocomplete="off" value="" class="form-control note-title" tabindex="1">
|
|
||||||
|
|
||||||
<button type="button" class="close-detail-button action-button d-sm-none d-md-none d-lg-none d-xl-none" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="note-detail-printable-wrapper">
|
|
||||||
<div class="note-detail-text note-detail-printable" tabindex="10000">
|
|
||||||
<div class="note-detail-text-editor"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="note-detail-code note-detail-printable">
|
|
||||||
<div class="note-detail-code-editor"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% include details/search.ejs %>
|
|
||||||
|
|
||||||
<% include details/render.ejs %>
|
|
||||||
|
|
||||||
<% include details/image.ejs %>
|
|
||||||
|
|
||||||
<% include details/relation_map.ejs %>
|
|
||||||
|
|
||||||
<% include details/protected_session_password.ejs %>
|
|
||||||
|
|
||||||
<% include details/book.ejs %>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<% include dialogs/protected_session_password.ejs %>
|
|
||||||
<% include dialogs/confirm.ejs %>
|
|
||||||
|
|
||||||
<div class="dropdown-menu dropdown-menu-sm" id="context-menu-container"></div>
|
|
||||||
|
|
||||||
<form action="logout" id="logout-form" method="POST" style="display: none;">
|
|
||||||
<input type="hidden" name="_csrf" value="<%= csrfToken %>"/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.baseApiUrl = 'api/';
|
window.baseApiUrl = 'api/';
|
||||||
@ -121,10 +46,8 @@
|
|||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="libraries/boxicons/css/boxicons.min.css">
|
<link rel="stylesheet" type="text/css" href="libraries/boxicons/css/boxicons.min.css">
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script>
|
||||||
// we hide container initally because otherwise it is rendered first without CSS and then flickers into
|
$("body").show();
|
||||||
// final form which is pretty ugly.
|
|
||||||
$("#container-row").show();
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
x
Reference in New Issue
Block a user