mirror of
https://github.com/ajurna/cbwebreader.git
synced 2025-12-06 14:17:19 +00:00
Merge branch 'csp_hell'
# Conflicts: # static/css/base.min.css
This commit is contained in:
@@ -50,6 +50,7 @@ MIDDLEWARE = [
|
|||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
|
'csp.middleware.CSPMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = "cbreader.urls"
|
ROOT_URLCONF = "cbreader.urls"
|
||||||
@@ -145,3 +146,11 @@ BOOTSTRAP4 = {
|
|||||||
"crossorigin": "anonymous",
|
"crossorigin": "anonymous",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
CSP_DEFAULT_SRC = ("'none'")
|
||||||
|
CSP_STYLE_SRC = ("'self'", 'cdn.jsdelivr.net', 'cdn.datatables.net')
|
||||||
|
CSP_IMG_SRC = ("'self'", "data:")
|
||||||
|
CSP_FONT_SRC = ("'self'")
|
||||||
|
CSP_SCRIPT_SRC = ("'self'", 'code.jquery.com', 'cdn.jsdelivr.net', 'cdn.datatables.net')
|
||||||
|
CSP_CONNECT_SRC = ("'self'")
|
||||||
|
CSP_INCLUDE_NONCE_IN = ['script-src']
|
||||||
|
CSP_SCRIPT_SRC_ATTR = ("'self'", "'unsafe-inline'")
|
||||||
@@ -2,7 +2,6 @@ import io
|
|||||||
import mimetypes
|
import mimetypes
|
||||||
import uuid
|
import uuid
|
||||||
import zipfile
|
import zipfile
|
||||||
from dataclasses import dataclass
|
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from itertools import zip_longest
|
from itertools import zip_longest
|
||||||
from os import listdir
|
from os import listdir
|
||||||
@@ -13,7 +12,7 @@ import fitz
|
|||||||
import rarfile
|
import rarfile
|
||||||
from PIL import Image, UnidentifiedImageError
|
from PIL import Image, UnidentifiedImageError
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User, AbstractUser
|
||||||
from django.core.files.uploadedfile import InMemoryUploadedFile
|
from django.core.files.uploadedfile import InMemoryUploadedFile
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.transaction import atomic
|
from django.db.transaction import atomic
|
||||||
@@ -230,18 +229,12 @@ class ComicBook(models.Model):
|
|||||||
def page_count(self):
|
def page_count(self):
|
||||||
return ComicPage.objects.filter(Comic=self).count()
|
return ComicPage.objects.filter(Comic=self).count()
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Navigation:
|
|
||||||
next_path: str
|
|
||||||
prev_path: str
|
|
||||||
cur_path: str
|
|
||||||
|
|
||||||
def nav(self, user):
|
def nav(self, user):
|
||||||
return self.Navigation(
|
return {
|
||||||
next_path=self.nav_get_next_comic(user),
|
"next_path": self.nav_get_next_comic(user),
|
||||||
prev_path=self.nav_get_prev_comic(user),
|
"prev_path": self.nav_get_prev_comic(user),
|
||||||
cur_path=urlsafe_base64_encode(self.selector.bytes)
|
"cur_path": urlsafe_base64_encode(self.selector.bytes)
|
||||||
)
|
}
|
||||||
|
|
||||||
def nav_get_prev_comic(self, user) -> str:
|
def nav_get_prev_comic(self, user) -> str:
|
||||||
base_dir = settings.COMIC_BOOK_VOLUME
|
base_dir = settings.COMIC_BOOK_VOLUME
|
||||||
|
|||||||
@@ -11,13 +11,13 @@
|
|||||||
<meta name="author" content="Ajurna">
|
<meta name="author" content="Ajurna">
|
||||||
<link rel="icon" href="{% static "favicon.ico" %}">
|
<link rel="icon" href="{% static "favicon.ico" %}">
|
||||||
|
|
||||||
<title>{% block title %}CB Reader{% endblock %}</title>
|
<title>{% block title %}CB Web Reader{% endblock %}</title>
|
||||||
|
|
||||||
<!-- Bootstrap core CSS -->
|
<!-- Bootstrap core CSS -->
|
||||||
{% bootstrap_css %}
|
{% bootstrap_css %}
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/bs4/dt-1.10.21/b-1.6.2/b-colvis-1.6.2/r-2.2.4/datatables.min.css"/>
|
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/bs4/dt-1.10.21/b-1.6.2/b-colvis-1.6.2/r-2.2.4/datatables.min.css"/>
|
||||||
<!-- Custom styles for this template -->
|
<!-- Custom styles for this template -->
|
||||||
<link href="{% static "css/base.css" %}" rel="stylesheet">
|
<link href="{% static "css/base.min.css" %}" rel="stylesheet">
|
||||||
<link href="{% static "font-awesome/css/all.css" %}" rel="stylesheet">
|
<link href="{% static "font-awesome/css/all.css" %}" rel="stylesheet">
|
||||||
|
|
||||||
{# <link href="{% static "reveal.js/css/reveal.css" %}" rel="stylesheet">#}
|
{# <link href="{% static "reveal.js/css/reveal.css" %}" rel="stylesheet">#}
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
<a class="navbar-brand" href="/"><img src="{% static 'img/logo.svg' %}" class="d-inline-block align-top" height="35px"> Web Reader</a>
|
<a class="navbar-brand" href="/"><img src="{% static 'img/logo.svg' %}" class="d-inline-block align-top" height="35px" alt="CB"> Web Reader</a>
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<!-- /.container -->
|
<!-- /.container -->
|
||||||
<footer class="footer mt-auto py-3">
|
<footer class="footer mt-auto py-3">
|
||||||
<div class="container text-center">
|
<div class="container text-center">
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons Licence" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/InteractiveResource" property="dct:title" rel="dct:type">CBReader</span> by <span xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName">Ajurna</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.<br />Based on a work at <a xmlns:dct="http://purl.org/dc/terms/" href="https://github.com/ajurna/cbreader" rel="dct:source">https://github.com/ajurna/cbreader</a>.
|
<a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons Licence" src="{% static "img/ccbysa.png" %}" /></a><br /><span xmlns:dct="https://purl.org/dc/terms/" href="https://purl.org/dc/dcmitype/InteractiveResource" property="dct:title" rel="dct:type">CBReader</span> by <span xmlns:cc="https://creativecommons.org/ns#" property="cc:attributionName">Ajurna</span> is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.<br />Based on a work at <a xmlns:dct="https://purl.org/dc/terms/" href="https://github.com/ajurna/cbreader" rel="dct:source">https://github.com/ajurna/cbreader</a>.
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" id="quicksearch" class="form-control" placeholder="Search" aria-label="Search list of comics" aria-describedby="button-addon4">
|
<input type="text" id="quicksearch" class="form-control" placeholder="Search" aria-label="Search list of comics" aria-describedby="button-addon4">
|
||||||
<div id="filters" class="input-group-append" id="button-addon4">
|
<div id="filters" class="input-group-append">
|
||||||
<button class="btn btn-outline-secondary filters" type="button" data-filter="*">All</button>
|
<button class="btn btn-outline-secondary filters" type="button" data-filter="*">All</button>
|
||||||
<button class="btn btn-outline-secondary filters" type="button" data-filter=".read">Read</button>
|
<button class="btn btn-outline-secondary filters" type="button" data-filter=".read">Read</button>
|
||||||
<button class="btn btn-outline-secondary filters" type="button" data-filter=".unread">Unread</button>
|
<button class="btn btn-outline-secondary filters" type="button" data-filter=".unread">Unread</button>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
Actions
|
Actions
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
||||||
<button type="button" class="btn btn-primary dropdown-item" title="Mark Un-Read" onclick="comic_action('{{ selector }}', 'Directory', 'mark_unread')"><i class="fas fa-book">Mark Un-Read</i></button>
|
<button type="button" class="btn btn-primary dropdown-item comic_action" title="Mark Un-Read" selector="{{ selector }}" itemtype="Directory" comic_action="mark_unread"><i class="fas fa-book">Mark Un-Read</i></button>
|
||||||
<button type="button" class="btn btn-primary dropdown-item" title="Mark Read" onclick="comic_action('{{ selector }}', 'Directory', 'mark_read')"><i class="fas fa-book-open">Mark Read</i></button>
|
<button type="button" class="btn btn-primary dropdown-item comic_action" title="Mark Read" selector="{{ selector }}" itemtype="Directory" comic_action="mark_read"><i class="fas fa-book-open">Mark Read</i></button>
|
||||||
{# <button type="button" class="btn btn-primary dropdown-item" title="Edit Comic"><i class="fas fa-edit">Edit Comic</i></button>#}
|
{# <button type="button" class="btn btn-primary dropdown-item" title="Edit Comic"><i class="fas fa-edit">Edit Comic</i></button>#}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -27,10 +27,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container comic-container">
|
<div class="container comic-container">
|
||||||
<div class="row grid">
|
<div class="row grid ">
|
||||||
{% for file in files %}
|
{% for file in files %}
|
||||||
<div class="m-2 grid-item {% if file.percent == 100 %}read{% else %}unread{% endif %}">
|
<div class="m-2 grid-item {% if file.percent == 100 %}read{% else %}unread{% endif %}">
|
||||||
<div class="card" style="width: 200px;">
|
<div class="card card_list_card">
|
||||||
{% if file.item_type == 'Directory' %}
|
{% if file.item_type == 'Directory' %}
|
||||||
<a href="{% url "comic_list" file.selector %}">
|
<a href="{% url "comic_list" file.selector %}">
|
||||||
{% elif file.item_type == 'ComicBook' %}
|
{% elif file.item_type == 'ComicBook' %}
|
||||||
@@ -38,12 +38,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if file.obj.thumbnail %}
|
{% if file.obj.thumbnail %}
|
||||||
<img src="{{file.obj.thumbnail.url}}" class="card-img-top" alt="{{ file.name }}" onerror="this.onerror=null;this.src='{% static "img/placeholder.png" %}';">
|
<img src="{{file.obj.thumbnail.url}}" class="card-img-top" alt="{{ file.name }}" alt_src="{% static "/img/placeholder.png" %}" onerror="this.onerror=null;this.src=this.getAttribute('alt_src');">
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if file.item_type == 'Directory' %}
|
{% if file.item_type == 'Directory' %}
|
||||||
<img src="{% url 'directory_thumbnail' file.selector %}" class="card-img-top" alt="{{ file.name }}" onerror="this.onerror=null;this.src='{% static "img/placeholder.png" %}';">
|
<img src="{% url 'directory_thumbnail' file.selector %}" class="card-img-top" alt="{{ file.name }}" alt_src="{% static "/img/placeholder.png" %}" onerror="this.onerror=null;this.src=this.getAttribute('alt_src');">
|
||||||
{% elif file.item_type == 'ComicBook' %}
|
{% elif file.item_type == 'ComicBook' %}
|
||||||
<img src="{% url 'comic_thumbnail' file.selector %}" class="card-img-top" alt="{{ file.name }}" onerror="this.onerror=null;this.src='{% static "img/placeholder.png" %}';">
|
<img src="{% url 'comic_thumbnail' file.selector %}" class="card-img-top" alt="{{ file.name }}" alt_src="{% static "/img/placeholder.png" %}" onerror="this.onerror=null;this.src=this.getAttribute('alt_src');">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
@@ -58,28 +58,33 @@
|
|||||||
</a>
|
</a>
|
||||||
</h5>
|
</h5>
|
||||||
<p class="card-text">
|
<p class="card-text">
|
||||||
|
<figure class="text-center w-100 mb-0">{{ file.total_read }} / {{ file.total }}</figure>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar" role="progressbar" style="width: {{ file.percent }}%;" aria-valuenow="{{ file.percent }}" aria-valuemin="0" aria-valuemax="100">{{ file.percent }}%</div>
|
<div class="progress-bar" role="progressbar" aria-valuenow="{{ file.percent }}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
<div class="btn-group" role="group" aria-label="Comic Actions">
|
<div class="btn-group" role="group" aria-label="Comic Actions">
|
||||||
<button type="button" class="btn btn-primary" title="Mark Un-Read" onclick="comic_action('{{ file.selector }}', '{{ file.item_type }}', 'mark_unread')"><i class="fas fa-book"></i></button>
|
<button type="button" class="btn btn-primary comic_action" title="Mark Un-Read" selector="{{ file.selector }}" itemtype="{{ file.item_type }}" comic_action="mark_unread"><i class="fas fa-book"></i></button>
|
||||||
<button type="button" class="btn btn-primary" title="Mark Read" onclick="comic_action('{{ file.selector }}', '{{ file.item_type }}', 'mark_read')"><i class="fas fa-book-open"></i></button>
|
<button type="button" class="btn btn-primary comic_action" title="Mark Read" selector="{{ file.selector }}" itemtype="{{ file.item_type }}" comic_action="mark_read"><i class="fas fa-book-open"></i></button>
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button id="btnGroupDrop1" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<button id="btnGroupDrop1" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
<div class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
||||||
<button type="button" class="btn btn-primary dropdown-item" title="Mark Un-Read" onclick="comic_action('{{ file.selector }}', '{{ file.item_type }}', 'mark_unread')"><i class="fas fa-book">Mark Un-Read</i></button>
|
<button type="button" class="btn btn-primary dropdown-item comic_action" title="Mark Un-Read" selector="{{ file.selector }}" itemtype="{{ file.item_type }}" comic_action="mark_unread"><i class="fas fa-book">Mark Un-Read</i></button>
|
||||||
<button type="button" class="btn btn-primary dropdown-item" title="Mark Read" onclick="comic_action('{{ file.selector }}', '{{ file.item_type }}', 'mark_read')"><i class="fas fa-book-open">Mark Read</i></button>
|
<button type="button" class="btn btn-primary dropdown-item comic_action" title="Mark Read" selector="{{ file.selector }}" itemtype="{{ file.item_type }}" comic_action="mark_read"><i class="fas fa-book-open">Mark Read</i></button>
|
||||||
{% if file.item_type != 'Directory' %}
|
{% if file.item_type != 'Directory' %}
|
||||||
<button type="button" class="btn btn-primary dropdown-item" title="Mark Previous Read"><i class="fas fa-book" onclick="comic_action('{{ file.selector }}', '{{ file.item_type }}', 'mark_previous')"><i class="fas fa-arrow-up">Mark Previous Read</i></i></button>
|
<button type="button" class="btn btn-primary dropdown-item comic_action" title="Mark Previous Read" selector="{{ file.selector }}" itemtype="{{ file.item_type }}" comic_action="mark_previous"><i class="fas fa-book"><i class="fas fa-arrow-up">Mark Previous Read</i></i></button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# <button type="button" class="btn btn-primary dropdown-item" title="Edit Comic"><i class="fas fa-edit">Edit Comic</i></button>#}
|
{# <button type="button" class="btn btn-primary dropdown-item" title="Edit Comic"><i class="fas fa-edit">Edit Comic</i></button>#}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{% if file.total_unread and file.item_type == 'Directory' %}
|
||||||
|
<span class="badge rounded-pill bg-primary card-badge">{{ file.total_unread }}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,76 +95,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block script %}
|
{% block script %}
|
||||||
<script>
|
<script type="text/javascript" src="{% static "js/comic_list.min.js" %}"></script>
|
||||||
|
|
||||||
var qsRegex;
|
|
||||||
var buttonFilter;
|
|
||||||
var $grid = $('.comic-container').isotope({
|
|
||||||
itemSelector: '.grid-item',
|
|
||||||
layoutMode: 'fitRows',
|
|
||||||
filter: function() {
|
|
||||||
var $this = $(this);
|
|
||||||
var searchResult = qsRegex ? $this.text().match( qsRegex ) : true;
|
|
||||||
var buttonResult = buttonFilter ? $this.is( buttonFilter ) : true;
|
|
||||||
return searchResult && buttonResult;
|
|
||||||
{#return searchResult#}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$('#filters').on( 'click', 'button', function() {
|
|
||||||
buttonFilter = $( this ).attr('data-filter');
|
|
||||||
sessionStorage.setItem(window.location.href+"button", buttonFilter);
|
|
||||||
$grid.isotope();
|
|
||||||
});
|
|
||||||
|
|
||||||
var $quicksearch = $('#quicksearch').keyup( debounce( function() {
|
|
||||||
qsRegex = new RegExp($quicksearch.val(), 'gi');
|
|
||||||
sessionStorage.setItem(window.location.href+'text', $quicksearch.val());
|
|
||||||
$grid.isotope();
|
|
||||||
}) );
|
|
||||||
|
|
||||||
// debounce so filtering doesn't happen every millisecond
|
|
||||||
function debounce( fn, threshold ) {
|
|
||||||
var timeout;
|
|
||||||
threshold = threshold || 100;
|
|
||||||
return function debounced() {
|
|
||||||
clearTimeout( timeout );
|
|
||||||
var args = arguments;
|
|
||||||
var _this = this;
|
|
||||||
function delayed() {
|
|
||||||
fn.apply( _this, args );
|
|
||||||
}
|
|
||||||
timeout = setTimeout( delayed, threshold );
|
|
||||||
};
|
|
||||||
}
|
|
||||||
setInterval(function (){
|
|
||||||
$grid.isotope();
|
|
||||||
}, 1000)
|
|
||||||
|
|
||||||
let field = document.getElementById("quicksearch");
|
|
||||||
|
|
||||||
// See if we have an autosave value
|
|
||||||
// (this will only happen if the page is accidentally refreshed)
|
|
||||||
if (sessionStorage.getItem(window.location.href+'text') || sessionStorage.getItem(window.location.href+'button')) {
|
|
||||||
// Restore the contents of the text field
|
|
||||||
field.value = sessionStorage.getItem(window.location.href+'text');
|
|
||||||
qsRegex = new RegExp($quicksearch.val(), 'gi');
|
|
||||||
buttonFilter = sessionStorage.getItem(window.location.href+'button');
|
|
||||||
$grid.isotope();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for changes in the text field
|
|
||||||
field.addEventListener("change", function() {
|
|
||||||
// And save the results into the session storage object
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
function comic_action(selector, item_type, action) {
|
|
||||||
$.ajax({
|
|
||||||
url: '/comic/action/' + action + '/' + item_type + '/' + selector + '/',
|
|
||||||
success: function (){window.location.reload()}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
{% block title %}{{ title }}{% endblock %}
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
|
||||||
<div class="reveal" id="comic_box">
|
<div class="reveal" id="comic_box">
|
||||||
<div class="slides" onclick="nextPage()">
|
<div id="slides_div" class="slides">
|
||||||
{% for page in pages %}
|
{% for page in pages %}
|
||||||
<section data-menu-title="{{ page.page_file_name }}">
|
<section data-menu-title="{{ page.page_file_name }}">
|
||||||
{% if page.content_type|first in 'image' %}
|
{% if page.content_type|first in 'image' %}
|
||||||
<img data-src="{% url "get_image" nav.cur_path page.index %}" class=" w-100" alt="{{ page.page_file_name }}">
|
<img data-src="{% url "get_image" nav.cur_path page.index %}" class=" w-100" alt="{{ page.page_file_name }}">
|
||||||
{% else %}
|
{% else %}
|
||||||
<p><embed type="{{ page.content_type }}" src="{% url "get_image" nav.cur_path page.index %}" onclick="nextPage()"></p>
|
<p><embed class="comic_embed" type="{{ page.content_type }}" src="{% url "get_image" nav.cur_path page.index %}"></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -21,65 +22,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block script %}
|
{% block script %}
|
||||||
<script>
|
{{ nav|json_script:"nav" }}
|
||||||
Reveal.initialize({
|
{{ status.last_read_page|json_script:"last_read_page" }}
|
||||||
controls: false,
|
<script type="text/javascript" src="{% static "js/read_comic.min.js" %}"></script>
|
||||||
hash: true,
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
margin: 0,
|
|
||||||
minScale: 1,
|
|
||||||
maxScale: 1,
|
|
||||||
disableLayout: true,
|
|
||||||
progress: true,
|
|
||||||
keyboard: {
|
|
||||||
37: () => {prevPage()},
|
|
||||||
39: () => {nextPage()},
|
|
||||||
38: () => {window.scrollTo({ top: window.scrollY-window.innerHeight*.6, left: 0, behavior: 'smooth' })},
|
|
||||||
40: () => {window.scrollTo({ top: window.scrollY+window.innerHeight*.6, left: 0, behavior: 'smooth' })},
|
|
||||||
},
|
|
||||||
touch: false,
|
|
||||||
transition: 'slide',
|
|
||||||
plugins: [ RevealMenu ]
|
|
||||||
}).then(() => {
|
|
||||||
Reveal.slide({{ status.last_read_page }})
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
Reveal.on( 'slidechanged', event => {
|
|
||||||
setTimeout(() =>{document.getElementsByClassName('slides')[0].scrollIntoView({behavior: 'smooth'})}, 100)
|
|
||||||
$.ajax({url: "/comic/set_page/{{nav.cur_path}}/" + event.indexh + "/"})
|
|
||||||
});
|
|
||||||
|
|
||||||
const hammertime = new Hammer(document.getElementById('comic_box'), {});
|
|
||||||
hammertime.on('swipeleft', function (ev) {
|
|
||||||
if (Reveal.isLastSlide()){
|
|
||||||
window.location = "{% url "read_comic" nav.next_path %}"
|
|
||||||
} else {
|
|
||||||
Reveal.next()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
hammertime.on('swiperight', function (ev) {
|
|
||||||
if (Reveal.isFirstSlide()){
|
|
||||||
window.location = "{% url "read_comic" nav.prev_path %}"
|
|
||||||
} else {
|
|
||||||
Reveal.prev();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function prevPage() {
|
|
||||||
if (Reveal.isFirstSlide()){
|
|
||||||
window.location = "{% url "read_comic" nav.prev_path %}"
|
|
||||||
} else {
|
|
||||||
Reveal.prev();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function nextPage() {
|
|
||||||
if (Reveal.isLastSlide()){
|
|
||||||
window.location = "{% url "read_comic" nav.next_path %}"
|
|
||||||
} else {
|
|
||||||
Reveal.next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -24,150 +24,7 @@
|
|||||||
|
|
||||||
{% block script %}
|
{% block script %}
|
||||||
<script type="text/javascript" src="{% static "pdfjs/build/pdf.js" %}"></script>
|
<script type="text/javascript" src="{% static "pdfjs/build/pdf.js" %}"></script>
|
||||||
<script>
|
{{ nav|json_script:"nav" }}
|
||||||
// If absolute URL from the remote server is provided, configure the CORS
|
{{ status.last_read_page|json_script:"last_read_page" }}
|
||||||
// header on that server.
|
<script type="text/javascript" src="{% static 'js/read_comic_pdf.min.js' %}"></script>
|
||||||
var url = '{% url "get_pdf" nav.cur_path %}';
|
|
||||||
|
|
||||||
// Loaded via <script> tag, create shortcut to access PDF.js exports.
|
|
||||||
var pdfjsLib = window['pdfjs-dist/build/pdf'];
|
|
||||||
|
|
||||||
|
|
||||||
// The workerSrc property shall be specified.
|
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = '{% static "pdfjs/build/pdf.worker.js" %}';
|
|
||||||
|
|
||||||
var pdfDoc = null,
|
|
||||||
pageNum = {{ status.last_read_page }},
|
|
||||||
pageRendering = false,
|
|
||||||
pageNumPending = null,
|
|
||||||
scale = 0.8,
|
|
||||||
canvas = document.getElementById('the-canvas'),
|
|
||||||
ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get page info from document, resize canvas accordingly, and render page.
|
|
||||||
* @param num Page number.
|
|
||||||
*/
|
|
||||||
function renderPage(num) {
|
|
||||||
pageRendering = true;
|
|
||||||
// Using promise to fetch the page
|
|
||||||
pdfDoc.getPage(num).then(function(page) {
|
|
||||||
let viewport = page.getViewport({scale: (window.innerWidth *.95) / page.getViewport({scale:1.0}).width});
|
|
||||||
canvas.height = viewport.height;
|
|
||||||
canvas.width = viewport.width;
|
|
||||||
|
|
||||||
// Render PDF page into canvas context
|
|
||||||
let renderContext = {
|
|
||||||
canvasContext: ctx,
|
|
||||||
viewport: viewport
|
|
||||||
};
|
|
||||||
let renderTask = page.render(renderContext);
|
|
||||||
|
|
||||||
// Wait for rendering to finish
|
|
||||||
renderTask.promise.then(function() {
|
|
||||||
pageRendering = false;
|
|
||||||
if (pageNumPending !== null) {
|
|
||||||
// New page rendering is pending
|
|
||||||
renderPage(pageNumPending);
|
|
||||||
pageNumPending = null;
|
|
||||||
}
|
|
||||||
}).then(function () {
|
|
||||||
document.getElementById('the-canvas').scrollIntoView({behavior: 'smooth'})
|
|
||||||
$.ajax({url: "/comic/set_page/{{nav.cur_path}}/" + (num-1) + "/"})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update page counters
|
|
||||||
document.getElementById('page_num').textContent = num;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If another page rendering in progress, waits until the rendering is
|
|
||||||
* finised. Otherwise, executes rendering immediately.
|
|
||||||
*/
|
|
||||||
function queueRenderPage(num) {
|
|
||||||
if (pageRendering) {
|
|
||||||
pageNumPending = num;
|
|
||||||
} else {
|
|
||||||
renderPage(num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays previous page.
|
|
||||||
*/
|
|
||||||
function onPrevPage() {
|
|
||||||
if (pageNum <= 1) {
|
|
||||||
window.location = "{% url "read_comic" nav.prev_path %}"
|
|
||||||
}
|
|
||||||
pageNum--;
|
|
||||||
queueRenderPage(pageNum);
|
|
||||||
}
|
|
||||||
document.getElementById('prev').addEventListener('click', onPrevPage);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays next page.
|
|
||||||
*/
|
|
||||||
function onNextPage() {
|
|
||||||
if (pageNum >= pdfDoc.numPages) {
|
|
||||||
window.location = "{% url "read_comic" nav.next_path %}"
|
|
||||||
}
|
|
||||||
pageNum++;
|
|
||||||
queueRenderPage(pageNum);
|
|
||||||
}
|
|
||||||
document.getElementById('next').addEventListener('click', onNextPage);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronously downloads PDF.
|
|
||||||
*/
|
|
||||||
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
|
|
||||||
pdfDoc = pdfDoc_;
|
|
||||||
document.getElementById('page_count').textContent = pdfDoc.numPages;
|
|
||||||
|
|
||||||
// Initial/first page rendering
|
|
||||||
renderPage(pageNum);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).keydown(function(e) { // add arrow key support
|
|
||||||
switch(e.which) {
|
|
||||||
case 37: // left
|
|
||||||
onPrevPage()
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 38: // up
|
|
||||||
window.scrollTo({
|
|
||||||
top: window.scrollY-window.innerHeight*.7,
|
|
||||||
left: 0,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 39: // right
|
|
||||||
onNextPage()
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 40: // down
|
|
||||||
window.scrollTo({
|
|
||||||
top: window.scrollY+window.innerHeight*.7,
|
|
||||||
left: 0,
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: return; // exit this handler for other keys
|
|
||||||
}
|
|
||||||
e.preventDefault(); // prevent the default action (scroll / move caret)
|
|
||||||
});
|
|
||||||
|
|
||||||
var hammertime = new Hammer(document.getElementById('the-canvas'), {});
|
|
||||||
hammertime.on('swipeleft', function () {
|
|
||||||
onNextPage()
|
|
||||||
})
|
|
||||||
hammertime.on('swiperight', function () {
|
|
||||||
onPrevPage()
|
|
||||||
})
|
|
||||||
hammertime.on('tap', function () {
|
|
||||||
onNextPage()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
{% block title %}{{ title }}{% endblock %}
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -40,83 +41,5 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block script %}
|
{% block script %}
|
||||||
<script>
|
<script type="text/javascript" src="{% static "js/recent_comics.min.js" %}"></script>
|
||||||
$(document).ready(function() {
|
|
||||||
|
|
||||||
var table = $('#comic_list').DataTable({
|
|
||||||
"processing": true,
|
|
||||||
"stateSave": true,
|
|
||||||
"serverSide": true,
|
|
||||||
"ajax": {
|
|
||||||
"type": "POST",
|
|
||||||
"url": "/comic/recent/json/",
|
|
||||||
"data": function ( d ) {
|
|
||||||
d.csrfmiddlewaretoken = Cookies.get('csrftoken');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"rowCallback": function( row, data, index ) {
|
|
||||||
var r = $(row);
|
|
||||||
var cols = $('td:nth-child(n+2)', row);
|
|
||||||
cols.attr('data-href', data['url']);
|
|
||||||
cols.attr('style', 'cursor: pointer;');
|
|
||||||
cols.click(function() {
|
|
||||||
window.document.location = $(this).data("href");
|
|
||||||
});
|
|
||||||
var tds = $('td:eq(0)', row);
|
|
||||||
tds.html('<input type="checkbox" name="selected" value="'+data['selector']+'" data-type="'+data['type']+'"/>');
|
|
||||||
var cb = $('input', tds);
|
|
||||||
cb.change(function() {
|
|
||||||
$(this).closest('tr').toggleClass('info')
|
|
||||||
});
|
|
||||||
|
|
||||||
},
|
|
||||||
"drawCallback": function( settings ) {
|
|
||||||
var tds = $('table tr td:first-child');
|
|
||||||
tds.click(function(event){
|
|
||||||
if (!$(event.target).is('input')) {
|
|
||||||
var $cb = $('input', this);
|
|
||||||
$cb.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
"columns": [
|
|
||||||
{ "data" : "selector", "orderable": false },
|
|
||||||
{ "data" : "icon", "orderable": false },
|
|
||||||
{ "data" : "name" },
|
|
||||||
{ "data" : "date" },
|
|
||||||
{ "data" : "label", "orderable": false },
|
|
||||||
],
|
|
||||||
|
|
||||||
"order": [[ 3, 'desc' ]],
|
|
||||||
});
|
|
||||||
$(".clickable-row").click(function() {
|
|
||||||
window.document.location = $(this).data("href");
|
|
||||||
});
|
|
||||||
$('#func_selector').on('change', function() {
|
|
||||||
$.post('/comic/edit/', $('#comic_form').serialize())
|
|
||||||
.done(function(){
|
|
||||||
$('#func_selector').val('choose');
|
|
||||||
$('#select-all input').prop('checked', false);
|
|
||||||
table.ajax.reload();
|
|
||||||
}).fail(function(){
|
|
||||||
alert('Error Submitting Change');
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
|
||||||
$('#select-all').click(function(event){
|
|
||||||
var cb = $('input', this);
|
|
||||||
if (!$(event.target).is('input')) {
|
|
||||||
cb.click();
|
|
||||||
}
|
|
||||||
$('table tr td:first-child input').each(function(chkbx) {
|
|
||||||
row = $(this);
|
|
||||||
if (row.prop('checked') != cb.prop('checked')){
|
|
||||||
row.click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} );
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -86,6 +86,9 @@ class DirFile:
|
|||||||
item_type: str = ''
|
item_type: str = ''
|
||||||
percent: int = 0
|
percent: int = 0
|
||||||
selector: str = ''
|
selector: str = ''
|
||||||
|
total: int = None
|
||||||
|
total_read: int = None
|
||||||
|
total_unread: int = None
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
self.item_type = type(self.obj).__name__
|
self.item_type = type(self.obj).__name__
|
||||||
@@ -94,10 +97,14 @@ class DirFile:
|
|||||||
total_adjustment = 1
|
total_adjustment = 1
|
||||||
if isinstance(self.obj, Directory):
|
if isinstance(self.obj, Directory):
|
||||||
total_adjustment = 0
|
total_adjustment = 0
|
||||||
|
self.total = self.obj.total - total_adjustment
|
||||||
|
self.total_read = self.obj.total_read
|
||||||
|
self.total_unread = self.total - self.total_read
|
||||||
try:
|
try:
|
||||||
self.percent = int((self.obj.total_read / (self.obj.total - total_adjustment)) * 100)
|
self.percent = int((self.obj.total_read / self.total) * 100)
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
self.percent = 0
|
self.percent = 0
|
||||||
|
|
||||||
self.selector = self.obj.url_safe_selector
|
self.selector = self.obj.url_safe_selector
|
||||||
if isinstance(self.obj, Directory):
|
if isinstance(self.obj, Directory):
|
||||||
self.name = self.obj.name
|
self.name = self.obj.name
|
||||||
|
|||||||
21
poetry.lock
generated
21
poetry.lock
generated
@@ -119,6 +119,21 @@ python-versions = ">=3.6"
|
|||||||
beautifulsoup4 = ">=4.8.0"
|
beautifulsoup4 = ">=4.8.0"
|
||||||
Django = ">=2.2"
|
Django = ">=2.2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-csp"
|
||||||
|
version = "3.7"
|
||||||
|
description = "Django Content Security Policy support."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
Django = ">=1.8"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
jinja2 = ["jinja2 (>=2.9.6)"]
|
||||||
|
tests = ["pytest (<4.0)", "pytest-django", "pytest-flakes (==1.0.1)", "pytest-pep8 (==1.0.6)", "pep8 (==1.4.6)", "mock (==1.0.1)", "six (==1.12.0)", "jinja2 (>=2.9.6)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-extensions"
|
name = "django-extensions"
|
||||||
version = "3.1.3"
|
version = "3.1.3"
|
||||||
@@ -455,7 +470,7 @@ dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.8"
|
python-versions = "^3.8"
|
||||||
content-hash = "c099b73f4400e26ba585774697d71eb475d22e365ad1ce9e6699086b30f403ad"
|
content-hash = "71642aa577156d70c6033dbc260a2ab03d247a17d9b0b0500a9c9a0e0228fd68"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
asgiref = [
|
asgiref = [
|
||||||
@@ -553,6 +568,10 @@ django-bootstrap4 = [
|
|||||||
{file = "django-bootstrap4-3.0.0.tar.gz", hash = "sha256:bffc96f65386fbd49cae1474393e01d4b414c12fcab0fff50545e6142e7ba19b"},
|
{file = "django-bootstrap4-3.0.0.tar.gz", hash = "sha256:bffc96f65386fbd49cae1474393e01d4b414c12fcab0fff50545e6142e7ba19b"},
|
||||||
{file = "django_bootstrap4-3.0.0-py3-none-any.whl", hash = "sha256:76a52fb22a8d3dbb2f7609b21908ce863e941a4462be079bf1d12025e551af37"},
|
{file = "django_bootstrap4-3.0.0-py3-none-any.whl", hash = "sha256:76a52fb22a8d3dbb2f7609b21908ce863e941a4462be079bf1d12025e551af37"},
|
||||||
]
|
]
|
||||||
|
django-csp = [
|
||||||
|
{file = "django_csp-3.7-py2.py3-none-any.whl", hash = "sha256:01443a07723f9a479d498bd7bb63571aaa771e690f64bde515db6cdb76e8041a"},
|
||||||
|
{file = "django_csp-3.7.tar.gz", hash = "sha256:01eda02ad3f10261c74131cdc0b5a6a62b7c7ad4fd017fbefb7a14776e0a9727"},
|
||||||
|
]
|
||||||
django-extensions = [
|
django-extensions = [
|
||||||
{file = "django-extensions-3.1.3.tar.gz", hash = "sha256:5f0fea7bf131ca303090352577a9e7f8bfbf5489bd9d9c8aea9401db28db34a0"},
|
{file = "django-extensions-3.1.3.tar.gz", hash = "sha256:5f0fea7bf131ca303090352577a9e7f8bfbf5489bd9d9c8aea9401db28db34a0"},
|
||||||
{file = "django_extensions-3.1.3-py3-none-any.whl", hash = "sha256:50de8977794a66a91575dd40f87d5053608f679561731845edbd325ceeb387e3"},
|
{file = "django_extensions-3.1.3-py3-none-any.whl", hash = "sha256:50de8977794a66a91575dd40f87d5053608f679561731845edbd325ceeb387e3"},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ line_length = 119
|
|||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "cbwebreader"
|
name = "cbwebreader"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
description = "CBR/Z Web Reader"
|
description = "CBR/Z Web Reader"
|
||||||
authors = ["ajurna <ajurna@gmail.com>"]
|
authors = ["ajurna <ajurna@gmail.com>"]
|
||||||
license = "Creative Commons Attribution-ShareAlike 4.0 International License"
|
license = "Creative Commons Attribution-ShareAlike 4.0 International License"
|
||||||
@@ -26,6 +26,7 @@ Pillow = "^8.2.0"
|
|||||||
django-imagekit = "^4.0.2"
|
django-imagekit = "^4.0.2"
|
||||||
PyMuPDF = "^1.18.12"
|
PyMuPDF = "^1.18.12"
|
||||||
django-bootstrap4 = "^3.0.0"
|
django-bootstrap4 = "^3.0.0"
|
||||||
|
django-csp = "^3.7"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
mypy = "^0.812"
|
mypy = "^0.812"
|
||||||
|
|||||||
@@ -1,46 +1,14 @@
|
|||||||
/*.navbar {*/
|
|
||||||
/* margin: 0px;*/
|
|
||||||
/*}*/
|
|
||||||
/*.starter-template {*/
|
|
||||||
/* padding: 40px 15px;*/
|
|
||||||
/* text-align: center;*/
|
|
||||||
/*}*/
|
|
||||||
/*!* Sticky footer styles*/
|
|
||||||
/*-------------------------------------------------- *!*/
|
|
||||||
/*html {*/
|
|
||||||
/* position: relative;*/
|
|
||||||
/* min-height: 100%;*/
|
|
||||||
/*}*/
|
|
||||||
/*body {*/
|
|
||||||
/* !* Margin bottom by footer height *!*/
|
|
||||||
/* margin-bottom: 80px;*/
|
|
||||||
/*}*/
|
|
||||||
/*.footer {*/
|
|
||||||
/* position: absolute;*/
|
|
||||||
/* bottom: 0;*/
|
|
||||||
/* width: 100%;*/
|
|
||||||
/* !* Set the fixed height of the footer here *!*/
|
|
||||||
/* height: 80px;*/
|
|
||||||
/* background-color: #f5f5f5;*/
|
|
||||||
/*}*/
|
|
||||||
/*.comic_box {*/
|
|
||||||
/* width: 100%;*/
|
|
||||||
/*}*/
|
|
||||||
|
|
||||||
/*#dropdown-list{*/
|
|
||||||
/* max-height: 300px;*/
|
|
||||||
/* overflow: auto;*/
|
|
||||||
/* box-shadow: none;*/
|
|
||||||
/* }*/
|
|
||||||
|
|
||||||
/* td a {*/
|
|
||||||
/* display:block;*/
|
|
||||||
/* width:100%;*/
|
|
||||||
/* }*/
|
|
||||||
/* tr.clickable-row {*/
|
|
||||||
/* cursor: pointer;*/
|
|
||||||
/* }*/
|
|
||||||
|
|
||||||
#comic_list caption {
|
#comic_list caption {
|
||||||
caption-side: top;
|
caption-side: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card_list_card {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.card .card-badge {
|
||||||
|
position:absolute;
|
||||||
|
top:10px;
|
||||||
|
left:10px;
|
||||||
|
padding:5px;
|
||||||
|
color:white;
|
||||||
|
}
|
||||||
3
static/css/base.min.css
vendored
3
static/css/base.min.css
vendored
@@ -1,2 +1 @@
|
|||||||
/*!* Sticky footer styles*/
|
#comic_list caption{caption-side:top}.card_list_card{width:200px}.card .card-badge{position:absolute;top:10px;left:10px;padding:5px;color:#fff}
|
||||||
#comic_list caption{caption-side:top}
|
|
||||||
BIN
static/img/ccbysa.png
Normal file
BIN
static/img/ccbysa.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
86
static/js/comic_list.js
Normal file
86
static/js/comic_list.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
var qsRegex;
|
||||||
|
var buttonFilter;
|
||||||
|
var $grid = $('.comic-container').isotope({
|
||||||
|
itemSelector: '.grid-item',
|
||||||
|
layoutMode: 'fitRows',
|
||||||
|
filter: function() {
|
||||||
|
var $this = $(this);
|
||||||
|
var searchResult = qsRegex ? $this.text().match( qsRegex ) : true;
|
||||||
|
var buttonResult = buttonFilter ? $this.is( buttonFilter ) : true;
|
||||||
|
return searchResult && buttonResult;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('#filters').on( 'click', 'button', function() {
|
||||||
|
if (typeof $( this ).attr('data-filter') === "undefined") {
|
||||||
|
|
||||||
|
}else {
|
||||||
|
buttonFilter = $( this ).attr('data-filter');
|
||||||
|
sessionStorage.setItem(window.location.href+"button", buttonFilter);
|
||||||
|
$grid.isotope();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var $quicksearch = $('#quicksearch').keyup( debounce( function() {
|
||||||
|
qsRegex = new RegExp($quicksearch.val(), 'gi');
|
||||||
|
sessionStorage.setItem(window.location.href+'text', $quicksearch.val());
|
||||||
|
$grid.isotope();
|
||||||
|
}) );
|
||||||
|
|
||||||
|
// debounce so filtering doesn't happen every millisecond
|
||||||
|
function debounce( fn, threshold ) {
|
||||||
|
var timeout;
|
||||||
|
threshold = threshold || 100;
|
||||||
|
return function debounced() {
|
||||||
|
clearTimeout( timeout );
|
||||||
|
var args = arguments;
|
||||||
|
var _this = this;
|
||||||
|
function delayed() {
|
||||||
|
fn.apply( _this, args );
|
||||||
|
}
|
||||||
|
timeout = setTimeout( delayed, threshold );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
setInterval(function (){
|
||||||
|
$grid.isotope();
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
let field = document.getElementById("quicksearch");
|
||||||
|
|
||||||
|
// See if we have an autosave value
|
||||||
|
// (this will only happen if the page is accidentally refreshed)
|
||||||
|
if (sessionStorage.getItem(window.location.href+'text') || sessionStorage.getItem(window.location.href+'button')) {
|
||||||
|
// Restore the contents of the text field
|
||||||
|
field.value = sessionStorage.getItem(window.location.href+'text');
|
||||||
|
qsRegex = new RegExp($quicksearch.val(), 'gi');
|
||||||
|
buttonFilter = sessionStorage.getItem(window.location.href+'button');
|
||||||
|
$grid.isotope();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for changes in the text field
|
||||||
|
field.addEventListener("change", function() {
|
||||||
|
// And save the results into the session storage object
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function comic_action(selector, item_type, action) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/comic/action/' + action + '/' + item_type + '/' + selector + '/',
|
||||||
|
success: function (){window.location.reload()}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$( ".progress-bar" ).each(function( index ) {
|
||||||
|
let bar = $(this)
|
||||||
|
bar.css('width', bar.attr('aria-valuenow') + '%')
|
||||||
|
});
|
||||||
|
|
||||||
|
let comic_action_elements = document.getElementsByClassName('comic_action')
|
||||||
|
|
||||||
|
comic_action_elements.forEach(el => el.addEventListener('click', event => {
|
||||||
|
let target = $(event.target).closest('button')
|
||||||
|
let selector = target.attr('selector')
|
||||||
|
let item_type = target.attr('itemtype')
|
||||||
|
let action = target.attr('comic_action')
|
||||||
|
comic_action(selector, item_type, action)
|
||||||
|
}));
|
||||||
1
static/js/comic_list.min.js
vendored
Normal file
1
static/js/comic_list.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var qsRegex;var buttonFilter;var $grid=$(".comic-container").isotope({itemSelector:".grid-item",layoutMode:"fitRows",filter:function(){var $this=$(this);var searchResult=qsRegex?$this.text().match(qsRegex):true;var buttonResult=buttonFilter?$this.is(buttonFilter):true;return searchResult&&buttonResult}});$("#filters").on("click","button",function(){if(typeof $(this).attr("data-filter")==="undefined"){}else{buttonFilter=$(this).attr("data-filter");sessionStorage.setItem(window.location.href+"button",buttonFilter);$grid.isotope()}});var $quicksearch=$("#quicksearch").keyup(debounce(function(){qsRegex=new RegExp($quicksearch.val(),"gi");sessionStorage.setItem(window.location.href+"text",$quicksearch.val());$grid.isotope()}));function debounce(fn,threshold){var timeout;threshold=threshold||100;return function debounced(){clearTimeout(timeout);var args=arguments;var _this=this;function delayed(){fn.apply(_this,args)}timeout=setTimeout(delayed,threshold)}}setInterval(function(){$grid.isotope()},1e3);let field=document.getElementById("quicksearch");if(sessionStorage.getItem(window.location.href+"text")||sessionStorage.getItem(window.location.href+"button")){field.value=sessionStorage.getItem(window.location.href+"text");qsRegex=new RegExp($quicksearch.val(),"gi");buttonFilter=sessionStorage.getItem(window.location.href+"button");$grid.isotope()}field.addEventListener("change",function(){});function comic_action(selector,item_type,action){$.ajax({url:"/comic/action/"+action+"/"+item_type+"/"+selector+"/",success:function(){window.location.reload()}})}$(".progress-bar").each(function(index){let bar=$(this);bar.css("width",bar.attr("aria-valuenow")+"%")});let comic_action_elements=document.getElementsByClassName("comic_action");comic_action_elements.forEach(el=>el.addEventListener("click",event=>{let target=$(event.target).closest("button");let selector=target.attr("selector");let item_type=target.attr("itemtype");let action=target.attr("comic_action");comic_action(selector,item_type,action)}));
|
||||||
70
static/js/read_comic.js
Normal file
70
static/js/read_comic.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
const nav = JSON.parse(document.getElementById('nav').textContent);
|
||||||
|
const last_read_page = JSON.parse(document.getElementById('last_read_page').textContent);
|
||||||
|
|
||||||
|
Reveal.initialize({
|
||||||
|
controls: false,
|
||||||
|
hash: true,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
margin: 0,
|
||||||
|
minScale: 1,
|
||||||
|
maxScale: 1,
|
||||||
|
disableLayout: true,
|
||||||
|
progress: true,
|
||||||
|
keyboard: {
|
||||||
|
37: () => {prevPage()},
|
||||||
|
39: () => {nextPage()},
|
||||||
|
38: () => {window.scrollTo({ top: window.scrollY-window.innerHeight*.6, left: 0, behavior: 'smooth' })},
|
||||||
|
40: () => {window.scrollTo({ top: window.scrollY+window.innerHeight*.6, left: 0, behavior: 'smooth' })},
|
||||||
|
},
|
||||||
|
touch: false,
|
||||||
|
transition: 'slide',
|
||||||
|
plugins: [ RevealMenu ]
|
||||||
|
}).then(() => {
|
||||||
|
Reveal.slide(last_read_page)
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Reveal.on( 'slidechanged', event => {
|
||||||
|
setTimeout(() =>{document.getElementsByClassName('slides')[0].scrollIntoView({behavior: 'smooth'})}, 100)
|
||||||
|
$.ajax({url: "/comic/set_page/" + nav.cur_path + "/" + event.indexh + "/"})
|
||||||
|
});
|
||||||
|
|
||||||
|
const hammertime = new Hammer(document.getElementById('comic_box'), {});
|
||||||
|
hammertime.on('swipeleft', function (ev) {
|
||||||
|
if (Reveal.isLastSlide()){
|
||||||
|
window.location = "/comic/read/"+ nav.next_path +"/"
|
||||||
|
} else {
|
||||||
|
Reveal.next()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
hammertime.on('swiperight', function (ev) {
|
||||||
|
if (Reveal.isFirstSlide()){
|
||||||
|
window.location = "/comic/read/"+ nav.prev_path +"/"
|
||||||
|
} else {
|
||||||
|
Reveal.prev();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function prevPage() {
|
||||||
|
if (Reveal.isFirstSlide()){
|
||||||
|
window.location = "/comic/read/"+ nav.prev_path +"/"
|
||||||
|
} else {
|
||||||
|
Reveal.prev();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function nextPage() {
|
||||||
|
if (Reveal.isLastSlide()){
|
||||||
|
window.location = "/comic/read/"+ nav.next_path +"/"
|
||||||
|
} else {
|
||||||
|
Reveal.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let slides_div = document.getElementById('slides_div')
|
||||||
|
slides_div.addEventListener('click', nextPage)
|
||||||
|
|
||||||
|
let embeds = document.getElementsByClassName('comic_embed')
|
||||||
|
|
||||||
|
embeds.forEach(function (embed){
|
||||||
|
embed.addEventListener('click', nextPage)
|
||||||
|
})
|
||||||
1
static/js/read_comic.min.js
vendored
Normal file
1
static/js/read_comic.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
const nav=JSON.parse(document.getElementById("nav").textContent);const last_read_page=JSON.parse(document.getElementById("last_read_page").textContent);Reveal.initialize({controls:false,hash:true,width:"100%",height:"100%",margin:0,minScale:1,maxScale:1,disableLayout:true,progress:true,keyboard:{37:()=>{prevPage()},39:()=>{nextPage()},38:()=>{window.scrollTo({top:window.scrollY-window.innerHeight*.6,left:0,behavior:"smooth"})},40:()=>{window.scrollTo({top:window.scrollY+window.innerHeight*.6,left:0,behavior:"smooth"})}},touch:false,transition:"slide",plugins:[RevealMenu]}).then(()=>{Reveal.slide(last_read_page)});Reveal.on("slidechanged",event=>{setTimeout(()=>{document.getElementsByClassName("slides")[0].scrollIntoView({behavior:"smooth"})},100);$.ajax({url:"/comic/set_page/"+nav.cur_path+"/"+event.indexh+"/"})});const hammertime=new Hammer(document.getElementById("comic_box"),{});hammertime.on("swipeleft",function(ev){if(Reveal.isLastSlide()){window.location="/comic/read/"+nav.next_path+"/"}else{Reveal.next()}});hammertime.on("swiperight",function(ev){if(Reveal.isFirstSlide()){window.location="/comic/read/"+nav.prev_path+"/"}else{Reveal.prev()}});function prevPage(){if(Reveal.isFirstSlide()){window.location="/comic/read/"+nav.prev_path+"/"}else{Reveal.prev()}}function nextPage(){if(Reveal.isLastSlide()){window.location="/comic/read/"+nav.next_path+"/"}else{Reveal.next()}}let slides_div=document.getElementById("slides_div");slides_div.addEventListener("click",nextPage);let embeds=document.getElementsByClassName("comic_embed");embeds.forEach(function(embed){embed.addEventListener("click",nextPage)});
|
||||||
146
static/js/read_comic_pdf.js
Normal file
146
static/js/read_comic_pdf.js
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
// If absolute URL from the remote server is provided, configure the CORS
|
||||||
|
// header on that server.
|
||||||
|
const nav = JSON.parse(document.getElementById('nav').textContent);
|
||||||
|
const last_read_page = JSON.parse(document.getElementById('last_read_page').textContent);
|
||||||
|
var url = "/comic/read/" + nav.cur_path + "/pdf"
|
||||||
|
|
||||||
|
// Loaded via <script> tag, create shortcut to access PDF.js exports.
|
||||||
|
var pdfjsLib = window['pdfjs-dist/build/pdf'];
|
||||||
|
|
||||||
|
|
||||||
|
// The workerSrc property shall be specified.
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = '/static/pdfjs/build/pdf.worker.js';
|
||||||
|
|
||||||
|
var pdfDoc = null,
|
||||||
|
pageNum = last_read_page,
|
||||||
|
pageRendering = false,
|
||||||
|
pageNumPending = null,
|
||||||
|
scale = 0.8,
|
||||||
|
canvas = document.getElementById('the-canvas'),
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get page info from document, resize canvas accordingly, and render page.
|
||||||
|
* @param num Page number.
|
||||||
|
*/
|
||||||
|
function renderPage(num) {
|
||||||
|
pageRendering = true;
|
||||||
|
// Using promise to fetch the page
|
||||||
|
pdfDoc.getPage(num).then(function(page) {
|
||||||
|
let viewport = page.getViewport({scale: (window.innerWidth *.95) / page.getViewport({scale:1.0}).width});
|
||||||
|
canvas.height = viewport.height;
|
||||||
|
canvas.width = viewport.width;
|
||||||
|
|
||||||
|
// Render PDF page into canvas context
|
||||||
|
let renderContext = {
|
||||||
|
canvasContext: ctx,
|
||||||
|
viewport: viewport
|
||||||
|
};
|
||||||
|
let renderTask = page.render(renderContext);
|
||||||
|
|
||||||
|
// Wait for rendering to finish
|
||||||
|
renderTask.promise.then(function() {
|
||||||
|
pageRendering = false;
|
||||||
|
if (pageNumPending !== null) {
|
||||||
|
// New page rendering is pending
|
||||||
|
renderPage(pageNumPending);
|
||||||
|
pageNumPending = null;
|
||||||
|
}
|
||||||
|
}).then(function () {
|
||||||
|
document.getElementById('the-canvas').scrollIntoView({behavior: 'smooth'})
|
||||||
|
$.ajax({url: "/comic/set_page/" + nav.cur_path + "/" + (num-1) + "/"})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update page counters
|
||||||
|
document.getElementById('page_num').textContent = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If another page rendering in progress, waits until the rendering is
|
||||||
|
* finised. Otherwise, executes rendering immediately.
|
||||||
|
*/
|
||||||
|
function queueRenderPage(num) {
|
||||||
|
if (pageRendering) {
|
||||||
|
pageNumPending = num;
|
||||||
|
} else {
|
||||||
|
renderPage(num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays previous page.
|
||||||
|
*/
|
||||||
|
function onPrevPage() {
|
||||||
|
if (pageNum <= 1) {
|
||||||
|
window.location = "/comic/read/"+ nav.prev_path +"/"
|
||||||
|
}
|
||||||
|
pageNum--;
|
||||||
|
queueRenderPage(pageNum);
|
||||||
|
}
|
||||||
|
document.getElementById('prev').addEventListener('click', onPrevPage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays next page.
|
||||||
|
*/
|
||||||
|
function onNextPage() {
|
||||||
|
if (pageNum >= pdfDoc.numPages) {
|
||||||
|
window.location = "/comic/read/"+ nav.next_path +"/"
|
||||||
|
}
|
||||||
|
pageNum++;
|
||||||
|
queueRenderPage(pageNum);
|
||||||
|
}
|
||||||
|
document.getElementById('next').addEventListener('click', onNextPage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously downloads PDF.
|
||||||
|
*/
|
||||||
|
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
|
||||||
|
pdfDoc = pdfDoc_;
|
||||||
|
document.getElementById('page_count').textContent = pdfDoc.numPages;
|
||||||
|
|
||||||
|
// Initial/first page rendering
|
||||||
|
renderPage(pageNum);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).keydown(function(e) { // add arrow key support
|
||||||
|
switch(e.which) {
|
||||||
|
case 37: // left
|
||||||
|
onPrevPage()
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 38: // up
|
||||||
|
window.scrollTo({
|
||||||
|
top: window.scrollY-window.innerHeight*.7,
|
||||||
|
left: 0,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 39: // right
|
||||||
|
onNextPage()
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 40: // down
|
||||||
|
window.scrollTo({
|
||||||
|
top: window.scrollY+window.innerHeight*.7,
|
||||||
|
left: 0,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: return; // exit this handler for other keys
|
||||||
|
}
|
||||||
|
e.preventDefault(); // prevent the default action (scroll / move caret)
|
||||||
|
});
|
||||||
|
|
||||||
|
var hammertime = new Hammer(document.getElementById('the-canvas'), {});
|
||||||
|
hammertime.on('swipeleft', function () {
|
||||||
|
onNextPage()
|
||||||
|
})
|
||||||
|
hammertime.on('swiperight', function () {
|
||||||
|
onPrevPage()
|
||||||
|
})
|
||||||
|
hammertime.on('tap', function () {
|
||||||
|
onNextPage()
|
||||||
|
})
|
||||||
1
static/js/read_comic_pdf.min.js
vendored
Normal file
1
static/js/read_comic_pdf.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
const nav=JSON.parse(document.getElementById("nav").textContent);const last_read_page=JSON.parse(document.getElementById("last_read_page").textContent);var url="/comic/read/"+nav.cur_path+"/pdf";var pdfjsLib=window["pdfjs-dist/build/pdf"];pdfjsLib.GlobalWorkerOptions.workerSrc="/static/pdfjs/build/pdf.worker.js";var pdfDoc=null,pageNum=last_read_page,pageRendering=false,pageNumPending=null,scale=.8,canvas=document.getElementById("the-canvas"),ctx=canvas.getContext("2d");function renderPage(num){pageRendering=true;pdfDoc.getPage(num).then(function(page){let viewport=page.getViewport({scale:window.innerWidth*.95/page.getViewport({scale:1}).width});canvas.height=viewport.height;canvas.width=viewport.width;let renderContext={canvasContext:ctx,viewport:viewport};let renderTask=page.render(renderContext);renderTask.promise.then(function(){pageRendering=false;if(pageNumPending!==null){renderPage(pageNumPending);pageNumPending=null}}).then(function(){document.getElementById("the-canvas").scrollIntoView({behavior:"smooth"});$.ajax({url:"/comic/set_page/"+nav.cur_path+"/"+(num-1)+"/"})})});document.getElementById("page_num").textContent=num}function queueRenderPage(num){if(pageRendering){pageNumPending=num}else{renderPage(num)}}function onPrevPage(){if(pageNum<=1){window.location="/comic/read/"+nav.prev_path+"/"}pageNum--;queueRenderPage(pageNum)}document.getElementById("prev").addEventListener("click",onPrevPage);function onNextPage(){if(pageNum>=pdfDoc.numPages){window.location="/comic/read/"+nav.next_path+"/"}pageNum++;queueRenderPage(pageNum)}document.getElementById("next").addEventListener("click",onNextPage);pdfjsLib.getDocument(url).promise.then(function(pdfDoc_){pdfDoc=pdfDoc_;document.getElementById("page_count").textContent=pdfDoc.numPages;renderPage(pageNum)});$(document).keydown(function(e){switch(e.which){case 37:onPrevPage();break;case 38:window.scrollTo({top:window.scrollY-window.innerHeight*.7,left:0,behavior:"smooth"});break;case 39:onNextPage();break;case 40:window.scrollTo({top:window.scrollY+window.innerHeight*.7,left:0,behavior:"smooth"});break;default:return}e.preventDefault()});var hammertime=new Hammer(document.getElementById("the-canvas"),{});hammertime.on("swipeleft",function(){onNextPage()});hammertime.on("swiperight",function(){onPrevPage()});hammertime.on("tap",function(){onNextPage()});
|
||||||
75
static/js/recent_comics.js
Normal file
75
static/js/recent_comics.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
var table = $('#comic_list').DataTable({
|
||||||
|
"processing": true,
|
||||||
|
"stateSave": true,
|
||||||
|
"serverSide": true,
|
||||||
|
"ajax": {
|
||||||
|
"type": "POST",
|
||||||
|
"url": "/comic/recent/json/",
|
||||||
|
"data": function ( d ) {
|
||||||
|
d.csrfmiddlewaretoken = Cookies.get('csrftoken');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"rowCallback": function( row, data, index ) {
|
||||||
|
var r = $(row);
|
||||||
|
var cols = $('td:nth-child(n+2)', row);
|
||||||
|
cols.attr('data-href', data['url']);
|
||||||
|
cols.attr('style', 'cursor: pointer;');
|
||||||
|
cols.click(function() {
|
||||||
|
window.document.location = $(this).data("href");
|
||||||
|
});
|
||||||
|
var tds = $('td:eq(0)', row);
|
||||||
|
tds.html('<input type="checkbox" name="selected" value="'+data['selector']+'" data-type="'+data['type']+'"/>');
|
||||||
|
var cb = $('input', tds);
|
||||||
|
cb.change(function() {
|
||||||
|
$(this).closest('tr').toggleClass('info')
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
"drawCallback": function( settings ) {
|
||||||
|
var tds = $('table tr td:first-child');
|
||||||
|
tds.click(function(event){
|
||||||
|
if (!$(event.target).is('input')) {
|
||||||
|
var $cb = $('input', this);
|
||||||
|
$cb.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"columns": [
|
||||||
|
{ "data" : "selector", "orderable": false },
|
||||||
|
{ "data" : "icon", "orderable": false },
|
||||||
|
{ "data" : "name" },
|
||||||
|
{ "data" : "date" },
|
||||||
|
{ "data" : "label", "orderable": false },
|
||||||
|
],
|
||||||
|
|
||||||
|
"order": [[ 3, 'desc' ]],
|
||||||
|
});
|
||||||
|
$(".clickable-row").click(function() {
|
||||||
|
window.document.location = $(this).data("href");
|
||||||
|
});
|
||||||
|
$('#func_selector').on('change', function() {
|
||||||
|
$.post('/comic/edit/', $('#comic_form').serialize())
|
||||||
|
.done(function(){
|
||||||
|
$('#func_selector').val('choose');
|
||||||
|
$('#select-all input').prop('checked', false);
|
||||||
|
table.ajax.reload();
|
||||||
|
}).fail(function(){
|
||||||
|
alert('Error Submitting Change');
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
$('#select-all').click(function(event){
|
||||||
|
var cb = $('input', this);
|
||||||
|
if (!$(event.target).is('input')) {
|
||||||
|
cb.click();
|
||||||
|
}
|
||||||
|
$('table tr td:first-child input').each(function(chkbx) {
|
||||||
|
row = $(this);
|
||||||
|
if (row.prop('checked') != cb.prop('checked')){
|
||||||
|
row.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} );
|
||||||
1
static/js/recent_comics.min.js
vendored
Normal file
1
static/js/recent_comics.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
$(document).ready(function(){var table=$("#comic_list").DataTable({processing:true,stateSave:true,serverSide:true,ajax:{type:"POST",url:"/comic/recent/json/",data:function(d){d.csrfmiddlewaretoken=Cookies.get("csrftoken")}},rowCallback:function(row,data,index){var r=$(row);var cols=$("td:nth-child(n+2)",row);cols.attr("data-href",data["url"]);cols.attr("style","cursor: pointer;");cols.click(function(){window.document.location=$(this).data("href")});var tds=$("td:eq(0)",row);tds.html('<input type="checkbox" name="selected" value="'+data["selector"]+'" data-type="'+data["type"]+'"/>');var cb=$("input",tds);cb.change(function(){$(this).closest("tr").toggleClass("info")})},drawCallback:function(settings){var tds=$("table tr td:first-child");tds.click(function(event){if(!$(event.target).is("input")){var $cb=$("input",this);$cb.click()}})},columns:[{data:"selector",orderable:false},{data:"icon",orderable:false},{data:"name"},{data:"date"},{data:"label",orderable:false}],order:[[3,"desc"]]});$(".clickable-row").click(function(){window.document.location=$(this).data("href")});$("#func_selector").on("change",function(){$.post("/comic/edit/",$("#comic_form").serialize()).done(function(){$("#func_selector").val("choose");$("#select-all input").prop("checked",false);table.ajax.reload()}).fail(function(){alert("Error Submitting Change")})});$("#select-all").click(function(event){var cb=$("input",this);if(!$(event.target).is("input")){cb.click()}$("table tr td:first-child input").each(function(chkbx){row=$(this);if(row.prop("checked")!=cb.prop("checked")){row.click()}})})});
|
||||||
Reference in New Issue
Block a user