Merge pull request #138 from kiwix/split--contentManagerHtml

Split contentmanager.html
This commit is contained in:
Matthieu Gautier 2019-05-13 15:33:27 +02:00 committed by GitHub
commit 9a3a554e6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 326 additions and 328 deletions

View File

@ -2,5 +2,7 @@
<qresource prefix="/">
<file>js/vue.js</file>
<file>texts/_contentManager.html</file>
<file>css/_contentManager.css</file>
<file>js/_contentManager.js</file>
</qresource>
</RCC>

View File

@ -0,0 +1,149 @@
html, body {
padding: 0;
margin: 0;
height: 100%;
position: relative;
width: 100%;
overflow: hidden;
}
#app {
height: 100%;
position: relative;
}
*:focus {
outline: none;
}
#searchInput {
background-image: url('qrc:///icons/search.svg');
background-repeat: no-repeat;
background-position: left top;
background-size: 40px 40px;
padding: 0px;
margin: 0px;
padding-left: 45px;
height: 40px;
width: 90%;
border: 1px solid #EEE;
}
#bookTable {
position: relative;
height: calc(100% - 42px); /* 42px = 40px(height of #searchInput) + 2px(border) */
width: 100%;
}
#bookList {
height: calc(100% - 19px - 20px); /*19px the header size, 20px the header margin-top */
overflow-y:scroll;
overflow-x:hidden;
position: relative;
width: 100%
}
.tablerow,
.header {
display: flex;
flex-direction: row;
width: 100%;
}
.header {
color: #555;
margin-top: 20px;
}
.tablecell{
flex-basis:20%;
}
.cell0 {
flex-basis: 60px;
flex-grow: 0;
flex-shrink: 0;
}
.tablerow > .cell1 {
font-weight: bold;
}
.cell0 > img {
width: 24px;
height: 24px;
}
.cell1,
.cell2,
.cell3,
.cell4,
.cell5 {
flex-grow: 1;
}
summary::-webkit-details-marker {
display: none
}
summary {
height: 64px;
}
.book {
border-top: 1px solid #EEE;
padding: 10px;
}
button {
background: transparent;
border: 0px;
}
.tablerow button {
color: blue;
font-weight: bold;
font-size: 14px;
border: 0px;
background: transparent;
border-radius: 2px;
}
.tablerow button:hover {
color: white;
background: blue;
}
details:hover {
background-color: #d9e9ff;
cursor: pointer;
}
.menu {
/* width: 120px; */
box-shadow: 0 4px 5px 3px rgba(0, 0, 0, 0.2);
position: fixed;
display: none;
z-index: 99999999999999;
background-color: white;
}
.menu-options {
list-style: none;
padding: 0 0 0 0;
margin: 2px 0px 2px;
}
.menu-option {
font-weight: 500;
font-size: 14px;
padding: 10px 40px 10px 20px;
cursor: pointer;
}
.menu-option:hover {
background: rgba(0, 0, 0, 0.2);
}
.loader {
margin: 0 auto;
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
.do-not-display {
display: none;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

View File

@ -0,0 +1,173 @@
const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
function niceBytes(x){
var unitIndex = 0;
var n = parseInt(x, 10) || 0;
while(n >= 1024 && ++unitIndex)
n = n/1024;
return(n.toFixed(n >= 10 || unitIndex < 1 ? 0 : 2) + ' ' + units[unitIndex]);
}
function createDict(keys, values) {
var d = {}
for(var i=0; i<keys.length; i++) {
d[keys[i]] = values[i];
}
return d;
}
const BOOK_KEYS = ["id", "name", "path", "url", "size", "description", "title", "tags", "date", "faviconUrl", "faviconMimeType", "downloadId"];
function addBook(values) {
var b = createDict(BOOK_KEYS, values);
if (b.downloadId && !downloadUpdaters.hasOwnProperty(b.id)) {
downloadUpdaters[b.id] = setInterval(function() { getDownloadInfo(b.id); }, 1000);
}
app.books.push(b);
}
function onBooksChanged () {
app.books = [];
for(var i=0; i<contentManager.bookIds.length; i++) {
var id = contentManager.bookIds[i];
contentManager.getBookInfos(id, BOOK_KEYS, addBook);
}
app.displayedBooksNb = 20;
}
downloadUpdaters = {}
const DOWNLOAD_KEYS = ["id", "status", "followedBy", "path", "totalLength", "completedLength", "downloadSpeed", "verifiedLength"];
function getDownloadInfo(id) {
contentManager.updateDownloadInfos(id, DOWNLOAD_KEYS, function(values) {
if (values.length == 0) {
clearInterval(downloadUpdaters[id]);
return;
}
d = createDict(DOWNLOAD_KEYS, values);
if (d.status == "completed") {
clearInterval(downloadUpdaters[id]);
Vue.delete(app.downloads, id);
return;
}
Vue.set(app.downloads, id, createDict(DOWNLOAD_KEYS, values));
});
}
function displayLoadIcon(display) {
if (display) {
document.getElementById("load-icon").classList.remove("do-not-display")
document.getElementById("bookList").classList.add("do-not-display");
} else {
document.getElementById("load-icon").classList.add("do-not-display");
document.getElementById("bookList").classList.remove("do-not-display")
}
}
function init() {
new QWebChannel(qt.webChannelTransport, function(channel) {
contentManager = channel.objects.contentManager;
app = new Vue({
el: "#app",
data: {
contentManager: contentManager,
displayedBooksNb: 20,
books: [],
downloads: {}
},
methods: {
openBook : function(book) {
contentManager.openBook(book.id, function() {});
},
downloadBook : function(book) {
contentManager.downloadBook(book.id, function(did) {
if (did.length == 0)
return;
book.downloadId = did;
downloadUpdaters[book.id] = setInterval(function() { getDownloadInfo(book.id); }, 1000);
});
},
eraseBook : function(book) {
contentManager.eraseBook(book.id);
},
pauseBook : function(book) {
contentManager.pauseBook(book.id);
},
resumeBook : function(book) {
contentManager.resumeBook(book.id);
},
cancelBook : function(book) {
contentManager.cancelBook(book.id);
clearInterval(downloadUpdaters[book.id]);
Vue.delete(app.downloads, book.id);
},
displayedBooks : function(books, nb) {
var a = books.slice(0, nb);
return a;
},
getBookFromMousePosition : function() {
var elements = document.elementsFromPoint(mouseX, mouseY);
var bookId = null;
for(var i = 0; i < elements.length; i++) {
if (elements[i].localName == "summary" && elements[i].classList.contains("book-summary")) {
bookId = elements[i].id;
break;
}
}
var book = null;
for(var i = 0; i < app.books.length; i++) {
if (app.books[i]["id"] == bookId) {
book = app.books[i];
break;
}
}
return book;
},
niceBytes : niceBytes
}
});
contentManager.booksChanged.connect(onBooksChanged);
contentManager.pendingRequest.connect(displayLoadIcon);
onBooksChanged();
displayLoadIcon(false);
});
}
futurCall = null;
function setSearch(value) {
clearTimeout(futurCall);
futurCall = setTimeout(function(){contentManager.setSearch(value)}, 100);
}
function scrolled(e) {
if (e.offsetHeight + e.scrollTop >= e.scrollHeight) {
app.displayedBooksNb = Math.min(app.displayedBooksNb+20, app.books.length);
}
}
window.addEventListener("click", e => {
if (menuVisible)
displayMenu(null);
});
var mouseX, mouseY = 0;
window.addEventListener("contextmenu", e => {
e.preventDefault();
mouseX = e.pageX;
mouseY = e.pageY;
setContextMenuPosition();
var book = app.getBookFromMousePosition();
displayMenu(book);
});
var menuVisible = false;
function displayMenu(book) {
var menu = document.getElementById("menu");
menu.style.display = (book) ? "block" : "none";
menuVisible = (book) ? true : false;
if (!book)
return;
var localElement = document.getElementsByClassName("local-option")[0];
localElement.style.display = (book.path) ? "block" : "none";
};
function setContextMenuPosition() {
var menu = document.getElementById("menu");
menu.style.left = `${mouseX}px`;
menu.style.top = `${mouseY}px`;
};

View File

@ -3,334 +3,8 @@
<head>
<script src="qrc:///js/vue.js"></script>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script type="text/javascript">
const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
function niceBytes(x){
var unitIndex = 0;
var n = parseInt(x, 10) || 0;
while(n >= 1024 && ++unitIndex)
n = n/1024;
return(n.toFixed(n >= 10 || unitIndex < 1 ? 0 : 2) + ' ' + units[unitIndex]);
}
function createDict(keys, values) {
var d = {}
for(var i=0; i<keys.length; i++) {
d[keys[i]] = values[i];
}
return d;
}
const BOOK_KEYS = ["id", "name", "path", "url", "size", "description", "title", "tags", "date", "faviconUrl", "faviconMimeType", "downloadId"];
function addBook(values) {
var b = createDict(BOOK_KEYS, values);
if (b.downloadId && !downloadUpdaters.hasOwnProperty(b.id)) {
downloadUpdaters[b.id] = setInterval(function() { getDownloadInfo(b.id); }, 1000);
}
app.books.push(b);
}
function onBooksChanged () {
app.books = [];
for(var i=0; i<contentManager.bookIds.length; i++) {
var id = contentManager.bookIds[i];
contentManager.getBookInfos(id, BOOK_KEYS, addBook);
}
app.displayedBooksNb = 20;
}
downloadUpdaters = {}
const DOWNLOAD_KEYS = ["id", "status", "followedBy", "path", "totalLength", "completedLength", "downloadSpeed", "verifiedLength"];
function getDownloadInfo(id) {
contentManager.updateDownloadInfos(id, DOWNLOAD_KEYS, function(values) {
if (values.length == 0) {
clearInterval(downloadUpdaters[id]);
return;
}
d = createDict(DOWNLOAD_KEYS, values);
if (d.status == "completed") {
clearInterval(downloadUpdaters[id]);
Vue.delete(app.downloads, id);
return;
}
Vue.set(app.downloads, id, createDict(DOWNLOAD_KEYS, values));
});
}
function displayLoadIcon(display) {
if (display) {
document.getElementById("load-icon").classList.remove("do-not-display")
document.getElementById("bookList").classList.add("do-not-display");
} else {
document.getElementById("load-icon").classList.add("do-not-display");
document.getElementById("bookList").classList.remove("do-not-display")
}
}
function init() {
new QWebChannel(qt.webChannelTransport, function(channel) {
contentManager = channel.objects.contentManager;
app = new Vue({
el: "#app",
data: {
contentManager: contentManager,
displayedBooksNb: 20,
books: [],
downloads: {}
},
methods: {
openBook : function(book) {
contentManager.openBook(book.id, function() {});
},
downloadBook : function(book) {
contentManager.downloadBook(book.id, function(did) {
if (did.length == 0)
return;
book.downloadId = did;
downloadUpdaters[book.id] = setInterval(function() { getDownloadInfo(book.id); }, 1000);
});
},
eraseBook : function(book) {
contentManager.eraseBook(book.id);
},
pauseBook : function(book) {
contentManager.pauseBook(book.id);
},
resumeBook : function(book) {
contentManager.resumeBook(book.id);
},
cancelBook : function(book) {
contentManager.cancelBook(book.id);
clearInterval(downloadUpdaters[book.id]);
Vue.delete(app.downloads, book.id);
},
displayedBooks : function(books, nb) {
var a = books.slice(0, nb);
return a;
},
getBookFromMousePosition : function() {
var elements = document.elementsFromPoint(mouseX, mouseY);
var bookId = null;
for(var i = 0; i < elements.length; i++) {
if (elements[i].localName == "summary" && elements[i].classList.contains("book-summary")) {
bookId = elements[i].id;
break;
}
}
var book = null;
for(var i = 0; i < app.books.length; i++) {
if (app.books[i]["id"] == bookId) {
book = app.books[i];
break;
}
}
return book;
},
niceBytes : niceBytes
}
});
contentManager.booksChanged.connect(onBooksChanged);
contentManager.pendingRequest.connect(displayLoadIcon);
onBooksChanged();
displayLoadIcon(false);
});
}
futurCall = null;
function setSearch(value) {
clearTimeout(futurCall);
futurCall = setTimeout(function(){contentManager.setSearch(value)}, 100);
}
function scrolled(e) {
if (e.offsetHeight + e.scrollTop >= e.scrollHeight) {
app.displayedBooksNb = Math.min(app.displayedBooksNb+20, app.books.length);
}
}
window.addEventListener("click", e => {
if (menuVisible)
displayMenu(null);
});
var mouseX, mouseY = 0;
window.addEventListener("contextmenu", e => {
e.preventDefault();
mouseX = e.pageX;
mouseY = e.pageY;
setContextMenuPosition();
var book = app.getBookFromMousePosition();
displayMenu(book);
});
var menuVisible = false;
function displayMenu(book) {
var menu = document.getElementById("menu");
menu.style.display = (book) ? "block" : "none";
menuVisible = (book) ? true : false;
if (!book)
return;
var localElement = document.getElementsByClassName("local-option")[0];
localElement.style.display = (book.path) ? "block" : "none";
};
function setContextMenuPosition() {
var menu = document.getElementById("menu");
menu.style.left = `${mouseX}px`;
menu.style.top = `${mouseY}px`;
};
</script>
<style>
html, body {
padding: 0;
margin: 0;
height: 100%;
position: relative;
width: 100%;
overflow: hidden;
}
#app {
height: 100%;
position: relative;
}
*:focus {
outline: none;
}
#searchInput {
background-image: url('qrc:///icons/search.svg');
background-repeat: no-repeat;
background-position: left top;
background-size: 40px 40px;
padding: 0px;
margin: 0px;
padding-left: 45px;
height: 40px;
width: 90%;
border: 1px solid #EEE;
}
#bookTable {
position: relative;
height: calc(100% - 42px); /* 42px = 40px(height of #searchInput) + 2px(border) */
width: 100%;
}
#bookList {
height: calc(100% - 19px - 20px); /*19px the header size, 20px the header margin-top */
overflow-y:scroll;
overflow-x:hidden;
position: relative;
width: 100%
}
.tablerow,
.header {
display: flex;
flex-direction: row;
width: 100%;
}
.header {
color: #555;
margin-top: 20px;
}
.tablecell{
flex-basis:20%;
}
.cell0 {
flex-basis: 60px;
flex-grow: 0;
flex-shrink: 0;
}
.tablerow > .cell1 {
font-weight: bold;
}
.cell0 > img {
width: 24px;
height: 24px;
}
.cell1,
.cell2,
.cell3,
.cell4,
.cell5 {
flex-grow: 1;
}
summary::-webkit-details-marker {
display: none
}
summary {
height: 64px;
}
.book {
border-top: 1px solid #EEE;
padding: 10px;
}
button {
background: transparent;
border: 0px;
}
.tablerow button {
color: blue;
font-weight: bold;
font-size: 14px;
border: 0px;
background: transparent;
border-radius: 2px;
}
.tablerow button:hover {
color: white;
background: blue;
}
details:hover {
background-color: #d9e9ff;
cursor: pointer;
}
.menu {
/* width: 120px; */
box-shadow: 0 4px 5px 3px rgba(0, 0, 0, 0.2);
position: fixed;
display: none;
z-index: 99999999999999;
background-color: white;
}
.menu-options {
list-style: none;
padding: 0 0 0 0;
margin: 2px 0px 2px;
}
.menu-option {
font-weight: 500;
font-size: 14px;
padding: 10px 40px 10px 20px;
cursor: pointer;
}
.menu-option:hover {
background: rgba(0, 0, 0, 0.2);
}
.loader {
margin: 0 auto;
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
.do-not-display {
display: none;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<script src="qrc:///js/_contentManager.js"></script>
<link rel="stylesheet" type="text/css" href="qrc:///css/_contentManager.css"/>
</head>
<body onload="init()">
<div id="app">