minimal support for epub documents
189
pandora/document/epub.py
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
import os
|
||||
import xml.etree.ElementTree as ET
|
||||
import zipfile
|
||||
import re
|
||||
from urllib.parse import unquote
|
||||
import lxml.html
|
||||
from io import BytesIO
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from ox import strip_tags, decode_html, normalize_name
|
||||
|
||||
import logging
|
||||
logging.getLogger('PIL').setLevel(logging.ERROR)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_ratio(data):
|
||||
try:
|
||||
img = Image.open(BytesIO(data))
|
||||
return img.size[0]/img.size[1]
|
||||
except:
|
||||
return -1
|
||||
|
||||
|
||||
def normpath(path):
|
||||
return '/'.join(os.path.normpath(path).split(os.sep))
|
||||
|
||||
|
||||
def cover(path):
|
||||
logger.debug('cover %s', path)
|
||||
data = None
|
||||
try:
|
||||
z = zipfile.ZipFile(path)
|
||||
except zipfile.BadZipFile:
|
||||
logger.debug('invalid epub file %s', path)
|
||||
return data
|
||||
|
||||
def use(filename):
|
||||
logger.debug('using %s', filename)
|
||||
try:
|
||||
data = z.read(filename)
|
||||
except:
|
||||
return None
|
||||
r = get_ratio(data)
|
||||
if r < 0.3 or r > 2:
|
||||
return None
|
||||
return data
|
||||
|
||||
files = []
|
||||
for f in z.filelist:
|
||||
if f.filename == 'calibre-logo.png':
|
||||
continue
|
||||
if 'cover' in f.filename.lower() and f.filename.split('.')[-1] in ('jpg', 'jpeg', 'png'):
|
||||
return use(f.filename)
|
||||
files.append(f.filename)
|
||||
opf = [f for f in files if f.endswith('opf')]
|
||||
if opf:
|
||||
#logger.debug('opf: %s', z.read(opf[0]).decode())
|
||||
info = ET.fromstring(z.read(opf[0]))
|
||||
metadata = info.findall('{http://www.idpf.org/2007/opf}metadata')
|
||||
if metadata:
|
||||
metadata = metadata[0]
|
||||
manifest = info.findall('{http://www.idpf.org/2007/opf}manifest')
|
||||
if manifest:
|
||||
manifest = manifest[0]
|
||||
if metadata and manifest:
|
||||
for e in list(metadata):
|
||||
if e.tag == '{http://www.idpf.org/2007/opf}meta' and e.attrib.get('name') == 'cover':
|
||||
cover_id = e.attrib['content']
|
||||
for e in list(manifest):
|
||||
if e.attrib['id'] == cover_id:
|
||||
filename = unquote(e.attrib['href'])
|
||||
filename = normpath(os.path.join(os.path.dirname(opf[0]), filename))
|
||||
if filename in files:
|
||||
return use(filename)
|
||||
if manifest:
|
||||
images = [e for e in list(manifest) if 'image' in e.attrib['media-type']]
|
||||
if images:
|
||||
image_data = []
|
||||
for e in images:
|
||||
filename = unquote(e.attrib['href'])
|
||||
filename = normpath(os.path.join(os.path.dirname(opf[0]), filename))
|
||||
if filename in files:
|
||||
image_data.append(filename)
|
||||
if image_data:
|
||||
image_data.sort(key=lambda name: z.getinfo(name).file_size)
|
||||
return use(image_data[-1])
|
||||
for e in list(manifest):
|
||||
if 'html' in e.attrib['media-type']:
|
||||
filename = unquote(e.attrib['href'])
|
||||
filename = normpath(os.path.join(os.path.dirname(opf[0]), filename))
|
||||
html = z.read(filename).decode('utf-8', 'ignore')
|
||||
img = re.compile('<img.*?src="(.*?)"').findall(html)
|
||||
#svg image
|
||||
img += re.compile('<image.*?href="(.*?)"').findall(html)
|
||||
if img:
|
||||
img = unquote(img[0])
|
||||
img = normpath(os.path.join(os.path.dirname(filename), img))
|
||||
if img in files:
|
||||
return use(img)
|
||||
return data
|
||||
|
||||
|
||||
def info(epub):
|
||||
data = {}
|
||||
try:
|
||||
z = zipfile.ZipFile(epub)
|
||||
except zipfile.BadZipFile:
|
||||
logger.debug('invalid epub file %s', epub)
|
||||
return data
|
||||
files = [f.filename for f in z.filelist]
|
||||
opf = [f for f in files if f.endswith('opf')]
|
||||
if opf:
|
||||
info = ET.fromstring(z.read(opf[0]))
|
||||
metadata = info.findall('{http://www.idpf.org/2007/opf}metadata')
|
||||
if metadata:
|
||||
metadata = metadata[0]
|
||||
for e in list(metadata):
|
||||
if e.text and e.text.strip() and e.text not in ('unknown', 'none'):
|
||||
key = e.tag.split('}')[-1]
|
||||
key = {
|
||||
'creator': 'author',
|
||||
}.get(key, key)
|
||||
value = e.text.strip()
|
||||
if key == 'identifier':
|
||||
if value:
|
||||
data['isbn'] = value
|
||||
elif key == 'author':
|
||||
data[key] = value.split(', ')
|
||||
if len(data[key]) == 2 and max(len(d.split(' ')) for d in data[key]) == 1:
|
||||
data[key] = [normalize_name(', '.join(data[key]))]
|
||||
else:
|
||||
data[key] = value
|
||||
toc = [f for f in files if 'toc.ncx' in f]
|
||||
if toc:
|
||||
try:
|
||||
_toc = ET.fromstring(z.read(toc[0]))
|
||||
nav_map = _toc.find('{http://www.daisy.org/z3986/2005/ncx/}navMap')
|
||||
except:
|
||||
logger.debug('failed to parse toc', exc_info=True)
|
||||
nav_map = None
|
||||
if nav_map:
|
||||
contents = []
|
||||
for point in nav_map.findall('{http://www.daisy.org/z3986/2005/ncx/}navPoint'):
|
||||
label = point.find('{http://www.daisy.org/z3986/2005/ncx/}navLabel')
|
||||
if label:
|
||||
txt = list(label)[0].text
|
||||
if txt:
|
||||
contents.append(txt)
|
||||
if contents:
|
||||
data['tableofcontents'] = '\n'.join(contents).strip()
|
||||
if 'tableofcontents' not in data:
|
||||
guide = info.find('{http://www.idpf.org/2007/opf}guide')
|
||||
if guide:
|
||||
for ref in guide.findall('{http://www.idpf.org/2007/opf}reference'):
|
||||
if ref.attrib.get('type') == 'toc':
|
||||
filename = unquote(ref.attrib['href']).split('#')[0]
|
||||
filename = normpath(os.path.join(os.path.dirname(opf[0]), filename))
|
||||
if filename in files:
|
||||
toc = z.read(filename)
|
||||
if toc:
|
||||
doc = lxml.html.document_fromstring(toc)
|
||||
data['tableofcontents'] = '\n'.join([a.text_content() for a in doc.xpath('//a')]).strip()
|
||||
if 'description' in data:
|
||||
data['description'] = strip_tags(decode_html(data['description']))
|
||||
text = extract_text(epub)
|
||||
data['textsize'] = len(text)
|
||||
if 'date' in data and 'T' in data['date']:
|
||||
data['date'] = data['date'].split('T')[0]
|
||||
if 'language' in data and isinstance(data['language'], str):
|
||||
data['language'] = get_language(data['language'])
|
||||
for key in list(data):
|
||||
if isinstance(data[key], str) and not data[key].strip():
|
||||
del data[key]
|
||||
return data
|
||||
|
||||
def extract_text(path):
|
||||
data = ''
|
||||
z = zipfile.ZipFile(path)
|
||||
for f in z.filelist:
|
||||
if '/._' in f.filename or f.filename.startswith('._'):
|
||||
continue
|
||||
if 'META-INF' in f.filename:
|
||||
continue
|
||||
if f.filename.split('.')[-1] in ('html', 'xml', 'htm'):
|
||||
data += z.read(f.filename).decode('utf-8', 'ignore')
|
||||
return data
|
||||
|
||||
|
|
@ -5,6 +5,8 @@ import tempfile
|
|||
|
||||
from django.conf import settings
|
||||
|
||||
from . import epub
|
||||
|
||||
|
||||
logger = logging.getLogger('pandora.' + __name__)
|
||||
|
||||
|
|
@ -55,6 +57,8 @@ class FulltextMixin:
|
|||
if self.file:
|
||||
if self.extension == 'pdf':
|
||||
return extract_text(self.file.path)
|
||||
elif self.extension == 'epub':
|
||||
return epub.extract_text(self.file.path)
|
||||
elif self.extension in IMAGE_EXTENSIONS:
|
||||
return ocr_image(self.file.path)
|
||||
elif self.extension in CONVERT_EXTENSIONS:
|
||||
|
|
@ -184,6 +188,9 @@ class FulltextPageMixin(FulltextMixin):
|
|||
if self.document.file:
|
||||
if self.document.extension == 'pdf':
|
||||
return extract_text(self.document.file.path, self.page)
|
||||
elif self.extension == 'epub':
|
||||
# FIXME: is there a nice way to split that into pages
|
||||
return epub.extract_text(self.file.path)
|
||||
elif self.extension in IMAGE_EXTENSIONS:
|
||||
return ocr_image(self.document.file.path)
|
||||
elif self.extension == 'html':
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ from user.utils import update_groups
|
|||
from . import managers
|
||||
from . import utils
|
||||
from . import tasks
|
||||
from . import epub
|
||||
from .fulltext import FulltextMixin, FulltextPageMixin
|
||||
|
||||
User = get_user_model()
|
||||
|
|
@ -174,13 +175,15 @@ class Document(models.Model, FulltextMixin):
|
|||
if self.extension == 'pdf':
|
||||
prefix = 2
|
||||
value = self.pages
|
||||
elif self.extension == 'epub':
|
||||
prefix = 3
|
||||
value = self.pages
|
||||
elif self.extension == 'html':
|
||||
prefix = 1
|
||||
value = self.dimensions
|
||||
else:
|
||||
if self.extension == 'html':
|
||||
prefix = 1
|
||||
value = self.dimensions
|
||||
else:
|
||||
prefix = 0
|
||||
value = self.width * self.height
|
||||
prefix = 0
|
||||
value = self.width * self.height
|
||||
if value < 0:
|
||||
value = 0
|
||||
s.dimensions = ox.sort_string('%d' % prefix) + ox.sort_string('%d' % value)
|
||||
|
|
@ -390,7 +393,7 @@ class Document(models.Model, FulltextMixin):
|
|||
|
||||
@property
|
||||
def dimensions(self):
|
||||
if self.extension == 'pdf':
|
||||
if self.extension in ('pdf', 'epub'):
|
||||
return self.pages
|
||||
elif self.extension == 'html':
|
||||
return len(self.data.get('text', '').split(' '))
|
||||
|
|
@ -564,6 +567,13 @@ class Document(models.Model, FulltextMixin):
|
|||
path = os.path.join(folder, '%dp%d,%s.jpg' % (size, page, ','.join(map(str, crop))))
|
||||
if not os.path.exists(path):
|
||||
resize_image(src, path, size=size)
|
||||
elif self.extension == 'epub':
|
||||
path = os.path.join(folder, '1024.jpg')
|
||||
if os.path.exists(src) and not os.path.exists(path):
|
||||
data = epub.cover(src)
|
||||
if data:
|
||||
with open(path, "wb") as fd:
|
||||
fd.write(data)
|
||||
elif self.extension in ('jpg', 'png', 'gif', 'webp', 'heic', 'heif', 'cr2'):
|
||||
if os.path.exists(src):
|
||||
if size and page:
|
||||
|
|
@ -607,17 +617,24 @@ class Document(models.Model, FulltextMixin):
|
|||
self.width = -1
|
||||
self.height = -1
|
||||
self.pages = utils.pdfpages(self.file.path)
|
||||
elif self.extension == 'epub':
|
||||
thumb = self.thumbnail(1024)
|
||||
if thumb:
|
||||
self.width, self.height = open_image_rgb(thumb).size
|
||||
self.pages = 1
|
||||
elif self.width == -1:
|
||||
self.pages = -1
|
||||
self.width, self.height = open_image_rgb(self.file.path).size
|
||||
|
||||
def get_ratio(self):
|
||||
if self.extension == 'pdf':
|
||||
if self.extension in ('pdf', 'epub'):
|
||||
image = self.thumbnail(1024)
|
||||
try:
|
||||
size = Image.open(image).size
|
||||
except:
|
||||
size = [1, 1]
|
||||
elif self.extension == 'epub':
|
||||
size = [1, 1]
|
||||
else:
|
||||
if self.width > 0:
|
||||
size = self.resolution
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import mimetypes
|
|||
import os
|
||||
import re
|
||||
import unicodedata
|
||||
import zipfile
|
||||
|
||||
import ox
|
||||
from ox.utils import json
|
||||
|
|
@ -15,7 +16,7 @@ from oxdjango.shortcuts import render_to_json_response, get_object_or_404_json,
|
|||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.db.models import Count, Sum
|
||||
from django.http import HttpResponse
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.shortcuts import render
|
||||
|
||||
from item import utils
|
||||
|
|
@ -557,3 +558,24 @@ def document(request, fragment):
|
|||
context['url'] = request.build_absolute_uri('/documents/' + fragment)
|
||||
context['settings'] = settings
|
||||
return render(request, "document.html", context)
|
||||
|
||||
def epub(request, id, filename):
|
||||
document = get_document_or_404_json(request, id)
|
||||
if not document.access(request.user):
|
||||
raise Http404
|
||||
if document.extension != 'epub':
|
||||
raise Http404
|
||||
z = zipfile.ZipFile(document.file.path)
|
||||
if filename == '':
|
||||
context = {}
|
||||
context["epub"] = document
|
||||
return render(request, "epub.html", context)
|
||||
elif filename not in [f.filename for f in z.filelist]:
|
||||
raise Http404
|
||||
else:
|
||||
content_type = {
|
||||
'xpgt': 'application/vnd.adobe-page-template+xml'
|
||||
}.get(filename.split('.')[0], mimetypes.guess_type(filename)[0]) or 'text/plain'
|
||||
content = z.read(filename)
|
||||
response = HttpResponse(content, content_type=content_type)
|
||||
return response
|
||||
|
|
|
|||
176
pandora/templates/epub.html
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="no-js">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title></title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
<link rel="stylesheet" href="/static/epub.js/css/normalize.css?3">
|
||||
<link rel="stylesheet" href="/static/epub.js/css/main.css?3">
|
||||
<link rel="stylesheet" href="/static/epub.js/css/popup.css?3">
|
||||
<link rel="stylesheet" href="/static/epub.js/css/annotations.css?3">
|
||||
<style>
|
||||
.arrow {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: text;
|
||||
|
||||
}
|
||||
#metainfo {
|
||||
display: none !important;
|
||||
}
|
||||
#main {
|
||||
border-radius: 0px;
|
||||
-webkit-transition: -webkit-transform .4s, width .2s;
|
||||
-moz-transition: -webkit-transform .4s, width .2s;
|
||||
-ms-transition: -webkit-transform .4s, width .2s;
|
||||
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
-ms-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
#sidebar {
|
||||
background: #fff;
|
||||
}
|
||||
#panels a {
|
||||
visibility: hidden;
|
||||
width: 18px;
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
color: #444;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
#panels a::before {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#panels a:hover {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
#panels a:active {
|
||||
color: #999;
|
||||
margin: 1px 0 -1px 6px;
|
||||
}
|
||||
|
||||
#panels a.active,
|
||||
#panels a.active:hover {
|
||||
color: #999;
|
||||
}
|
||||
.list_item a {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.list_item.currentChapter > a,
|
||||
.list_item a:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* #tocView li.openChapter > a, */
|
||||
.list_item a:hover {
|
||||
color: #333;
|
||||
}
|
||||
#panels {
|
||||
padding-left: 14px;
|
||||
background: #eee;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
-ms-box-shadow: none;
|
||||
box-shadow: none; //0px 1px 3px rgba(0,0,0,.3);
|
||||
|
||||
}
|
||||
#divider.show {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="/static/oxjs/min/Ox.js?3"></script>
|
||||
<script src="/static/epub.js/js/libs/jquery.min.js?3"></script>
|
||||
<script src="/static/epub.js/js/libs/zip.min.js?3"></script>
|
||||
<script src="/static/reader/epub.js?3"></script>
|
||||
<!-- Render -->
|
||||
<script src="/static/epub.js/js/epub.js?3"></script>
|
||||
<!-- Reader -->
|
||||
<script src="/static/epub.js/js/reader.js?3"></script>
|
||||
|
||||
<!-- Plugins -->
|
||||
<!-- <script src="js/plugins/search.js"></script> -->
|
||||
<!-- Highlights -->
|
||||
<!-- <script src="/static/epub.js/js/hooks/extensions/highlight.js"></script> -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="sidebar">
|
||||
<div id="panels">
|
||||
<!--
|
||||
<input id="searchBox" placeholder="search" type="search">
|
||||
|
||||
<a id="show-Search" class="show_view icon-search" data-view="Search">Search</a>
|
||||
-->
|
||||
<a id="show-Toc" class="show_view icon-list-1 active" data-view="Toc">TOC</a>
|
||||
<a id="show-Bookmarks" class="show_view icon-bookmark" data-view="Bookmarks">Bookmarks</a>
|
||||
<!--
|
||||
<a id="show-Notes" class="show_view icon-edit" data-view="Notes">Notes</a>
|
||||
-->
|
||||
</div>
|
||||
<div id="tocView" class="view">
|
||||
</div>
|
||||
<div id="searchView" class="view">
|
||||
<ul id="searchResults"></ul>
|
||||
</div>
|
||||
<div id="bookmarksView" class="view">
|
||||
<ul id="bookmarks"></ul>
|
||||
</div>
|
||||
<div id="notesView" class="view">
|
||||
<div id="new-note">
|
||||
<textarea id="note-text"></textarea>
|
||||
<button id="note-anchor">Anchor</button>
|
||||
</div>
|
||||
<ol id="notes"></ol>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main">
|
||||
|
||||
<div id="titlebar">
|
||||
<div id="opener">
|
||||
<a id="slider" class="icon-menu">Menu</a>
|
||||
</div>
|
||||
<div id="metainfo">
|
||||
<span id="book-title"></span>
|
||||
<span id="title-seperator"> – </span>
|
||||
<span id="chapter-title"></span>
|
||||
</div>
|
||||
<div id="title-controls">
|
||||
<a id="bookmark" class="icon-bookmark-empty">Bookmark</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="divider"></div>
|
||||
<div id="prev" class="arrow">‹</div>
|
||||
<div id="viewer"></div>
|
||||
<div id="next" class="arrow">›</div>
|
||||
|
||||
<div id="loader"><img src="/static/epub.js/img/loader.gif"></div>
|
||||
</div>
|
||||
<div class="modal md-effect-1" id="settings-modal">
|
||||
<div class="md-content">
|
||||
<h3>Settings</h3>
|
||||
<div>
|
||||
<p>
|
||||
<input type="checkbox" id="sidebarReflow" name="sidebarReflow">Reflow text when sidebars are open.</input>
|
||||
</p>
|
||||
</div>
|
||||
<div class="closer icon-cancel-circled"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -53,6 +53,7 @@ urlpatterns += [
|
|||
re_path(r'^resetUI$', user.views.reset_ui),
|
||||
re_path(r'^collection/(?P<id>.*?)/icon(?P<size>\d*).jpg$', documentcollection.views.icon),
|
||||
re_path(r'^documents/(?P<id>[A-Z0-9]+)/(?P<size>\d*)p(?P<page>[\d,]*).jpg$', document.views.thumbnail),
|
||||
re_path(r'^documents/(?P<id>[A-Z0-9]+)/epub/(?P<filename>.*?)$', document.views.epub),
|
||||
re_path(r'^documents/(?P<id>[A-Z0-9]+)/(?P<name>.*?\.[^\d]{3,4})$', document.views.file),
|
||||
re_path(r'^documents/(?P<fragment>.*?)$', document.views.document),
|
||||
re_path(r'^edit/(?P<id>.*?)/icon(?P<size>\d*).jpg$', edit.views.icon),
|
||||
|
|
|
|||
3
static/epub.js/css/annotations.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.annotator-adder {
|
||||
width: 80px;
|
||||
}
|
||||
817
static/epub.js/css/main.css
Executable file
|
|
@ -0,0 +1,817 @@
|
|||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('../font/fontello.eot?60518104');
|
||||
src: url('../font/fontello.eot?60518104#iefix') format('embedded-opentype'),
|
||||
url('../font/fontello.woff?60518104') format('woff'),
|
||||
url('../font/fontello.ttf?60518104') format('truetype'),
|
||||
url('../font/fontello.svg?60518104#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #4e4e4e;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#main {
|
||||
/* height: 500px; */
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
right: 0;
|
||||
/* left: 40px; */
|
||||
/* -webkit-transform: translate(40px, 0);
|
||||
-moz-transform: translate(40px, 0); */
|
||||
|
||||
/* border-radius: 5px 0px 0px 5px; */
|
||||
border-radius: 5px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
-webkit-transition: -webkit-transform .4s, width .2s;
|
||||
-moz-transition: -webkit-transform .4s, width .2s;
|
||||
-ms-transition: -webkit-transform .4s, width .2s;
|
||||
|
||||
-moz-box-shadow: inset 0 0 50px rgba(0,0,0,.1);
|
||||
-webkit-box-shadow: inset 0 0 50px rgba(0,0,0,.1);
|
||||
-ms-box-shadow: inset 0 0 50px rgba(0,0,0,.1);
|
||||
box-shadow: inset 0 0 50px rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
|
||||
#titlebar {
|
||||
height: 8%;
|
||||
min-height: 20px;
|
||||
padding: 10px;
|
||||
/* margin: 0 50px 0 50px; */
|
||||
position: relative;
|
||||
color: #4f4f4f;
|
||||
font-weight: 100;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
opacity: .5;
|
||||
text-align: center;
|
||||
-webkit-transition: opacity .5s;
|
||||
-moz-transition: opacity .5s;
|
||||
-ms-transition: opacity .5s;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#titlebar:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#titlebar a {
|
||||
width: 18px;
|
||||
height: 19px;
|
||||
line-height: 20px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
opacity: .5;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#titlebar a::before {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#titlebar a:hover {
|
||||
opacity: .8;
|
||||
border: 1px rgba(0,0,0,.2) solid;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
#titlebar a:active {
|
||||
opacity: 1;
|
||||
color: rgba(0,0,0,.6);
|
||||
/* margin: 1px -1px -1px 1px; */
|
||||
-moz-box-shadow: inset 0 0 6px rgba(155,155,155,.8);
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(155,155,155,.8);
|
||||
-ms-box-shadow: inset 0 0 6px rgba(155,155,155,.8);
|
||||
box-shadow: inset 0 0 6px rgba(155,155,155,.8);
|
||||
}
|
||||
|
||||
#book-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#title-seperator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#viewer {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
/* margin-left: 10%; */
|
||||
margin: 0 auto;
|
||||
max-width: 1250px;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#viewer iframe {
|
||||
border: none;
|
||||
}
|
||||
|
||||
#prev {
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
#next {
|
||||
right: 40px;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -32px;
|
||||
font-size: 64px;
|
||||
color: #E2E2E2;
|
||||
font-family: arial, sans-serif;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.arrow:hover {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.arrow:active,
|
||||
.arrow.active {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
background: #6b6b6b;
|
||||
position: absolute;
|
||||
/* left: -260px; */
|
||||
/* -webkit-transform: translate(-260px, 0);
|
||||
-moz-transform: translate(-260px, 0); */
|
||||
top: 0;
|
||||
min-width: 300px;
|
||||
width: 25%;
|
||||
height: 100%;
|
||||
-webkit-transition: -webkit-transform .5s;
|
||||
-moz-transition: -moz-transform .5s;
|
||||
-ms-transition: -moz-transform .5s;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#sidebar.open {
|
||||
/* left: 0; */
|
||||
/* -webkit-transform: translate(0, 0);
|
||||
-moz-transform: translate(0, 0); */
|
||||
}
|
||||
|
||||
#main.closed {
|
||||
/* left: 300px; */
|
||||
-webkit-transform: translate(300px, 0);
|
||||
-moz-transform: translate(300px, 0);
|
||||
-ms-transform: translate(300px, 0);
|
||||
}
|
||||
|
||||
#main.single {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
#main.single #viewer {
|
||||
/* width: 60%;
|
||||
margin-left: 20%; */
|
||||
}
|
||||
|
||||
#panels {
|
||||
background: #4e4e4e;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
padding: 13px 0;
|
||||
height: 14px;
|
||||
-moz-box-shadow: 0px 1px 3px rgba(0,0,0,.6);
|
||||
-webkit-box-shadow: 0px 1px 3px rgba(0,0,0,.6);
|
||||
-ms-box-shadow: 0px 1px 3px rgba(0,0,0,.6);
|
||||
box-shadow: 0px 1px 3px rgba(0,0,0,.6);
|
||||
}
|
||||
|
||||
#opener {
|
||||
/* padding: 10px 10px; */
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* #opener #slider {
|
||||
width: 25px;
|
||||
} */
|
||||
|
||||
#metainfo {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
#title-controls {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#panels a {
|
||||
visibility: hidden;
|
||||
width: 18px;
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
color: #ccc;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
#panels a::before {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#panels a:hover {
|
||||
color: #AAA;
|
||||
}
|
||||
|
||||
#panels a:active {
|
||||
color: #AAA;
|
||||
margin: 1px 0 -1px 6px;
|
||||
}
|
||||
|
||||
#panels a.active,
|
||||
#panels a.active:hover {
|
||||
color: #AAA;
|
||||
}
|
||||
|
||||
#searchBox {
|
||||
width: 165px;
|
||||
float: left;
|
||||
margin-left: 10px;
|
||||
margin-top: -1px;
|
||||
/*
|
||||
border-radius: 5px;
|
||||
background: #9b9b9b;
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
margin-top: -5px;
|
||||
padding: 3px 10px;
|
||||
color: #000;
|
||||
border: none;
|
||||
outline: none; */
|
||||
|
||||
}
|
||||
|
||||
input::-webkit-input-placeholder {
|
||||
color: #454545;
|
||||
}
|
||||
input:-moz-placeholder {
|
||||
color: #454545;
|
||||
}
|
||||
input:-ms-placeholder {
|
||||
color: #454545;
|
||||
}
|
||||
|
||||
#divider {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
border-right: 1px #000 solid;
|
||||
height: 80%;
|
||||
z-index: 1;
|
||||
left: 50%;
|
||||
margin-left: -1px;
|
||||
top: 10%;
|
||||
opacity: .15;
|
||||
box-shadow: -2px 0 15px rgba(0, 0, 0, 1);
|
||||
display: none;
|
||||
}
|
||||
|
||||
#divider.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#loader {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin: -33px 0 0 -33px;
|
||||
}
|
||||
|
||||
#tocView,
|
||||
#bookmarksView {
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
min-width: 300px;
|
||||
width: 25%;
|
||||
height: 100%;
|
||||
visibility: hidden;
|
||||
-webkit-transition: visibility 0 ease .5s;
|
||||
-moz-transition: visibility 0 ease .5s;
|
||||
-ms-transition: visibility 0 ease .5s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#sidebar.open #tocView,
|
||||
#sidebar.open #bookmarksView {
|
||||
overflow-y: auto;
|
||||
visibility: visible;
|
||||
-webkit-transition: visibility 0 ease 0;
|
||||
-moz-transition: visibility 0 ease 0;
|
||||
-ms-transition: visibility 0 ease 0;
|
||||
}
|
||||
|
||||
#sidebar.open #tocView {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#tocView > ul,
|
||||
#bookmarksView > ul {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 50px;
|
||||
padding-left: 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#tocView li,
|
||||
#bookmarksView li {
|
||||
margin-bottom:10px;
|
||||
width: 225px;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
list-style: none;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
#tocView li:active,
|
||||
#tocView li.currentChapter
|
||||
{
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.list_item a {
|
||||
color: #AAA;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.list_item a.chapter {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.list_item a.section {
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
.list_item.currentChapter > a,
|
||||
.list_item a:hover {
|
||||
color: #f1f1f1
|
||||
}
|
||||
|
||||
/* #tocView li.openChapter > a, */
|
||||
.list_item a:hover {
|
||||
color: #E2E2E2;
|
||||
}
|
||||
|
||||
.list_item ul {
|
||||
padding-left:10px;
|
||||
margin-top: 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.list_item.currentChapter > ul,
|
||||
.list_item.openChapter > ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#tocView.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toc_toggle {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.toc_toggle:before {
|
||||
content: '▸';
|
||||
color: #fff;
|
||||
margin-right: -4px;
|
||||
}
|
||||
|
||||
.currentChapter > .toc_toggle:before,
|
||||
.openChapter > .toc_toggle:before {
|
||||
content: '▾';
|
||||
}
|
||||
|
||||
.view {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
display: none;
|
||||
padding-top: 50px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#searchResults {
|
||||
margin-bottom: 50px;
|
||||
padding-left: 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#searchResults li {
|
||||
margin-bottom:10px;
|
||||
width: 225px;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#searchResults a {
|
||||
color: #AAA;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#searchResults p {
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
#searchResults p .match {
|
||||
background: #ccc;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#searchResults li > p {
|
||||
color: #AAA;
|
||||
}
|
||||
|
||||
#searchResults li a:hover {
|
||||
color: #E2E2E2;
|
||||
}
|
||||
|
||||
#searchView.shown {
|
||||
display: block;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#notes {
|
||||
padding: 0 0 0 34px;
|
||||
}
|
||||
|
||||
#notes li {
|
||||
color: #eee;
|
||||
font-size: 12px;
|
||||
width: 240px;
|
||||
border-top: 1px #fff solid;
|
||||
padding-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
#notes li a {
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
#notes li a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#notes li img {
|
||||
max-width: 240px;
|
||||
}
|
||||
|
||||
#note-text {
|
||||
display: block;
|
||||
width: 260px;
|
||||
height: 80px;
|
||||
margin: 0 auto;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#note-text[disabled], #note-text[disabled="disabled"]{
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
#note-anchor {
|
||||
margin-left: 218px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#settingsPanel {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#settingsPanel h3 {
|
||||
color:#f1f1f1;
|
||||
font-family:Georgia, "Times New Roman", Times, serif;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
#settingsPanel ul {
|
||||
margin-top:60px;
|
||||
list-style-type:none;
|
||||
}
|
||||
|
||||
#settingsPanel li {
|
||||
font-size:1em;
|
||||
color:#f1f1f1;
|
||||
}
|
||||
|
||||
#settingsPanel .xsmall { font-size:x-small; }
|
||||
#settingsPanel .small { font-size:small; }
|
||||
#settingsPanel .medium { font-size:medium; }
|
||||
#settingsPanel .large { font-size:large; }
|
||||
#settingsPanel .xlarge { font-size:x-large; }
|
||||
|
||||
.highlight { background-color: yellow }
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 50%;
|
||||
width: 630px;
|
||||
|
||||
height: auto;
|
||||
z-index: 2000;
|
||||
visibility: hidden;
|
||||
margin-left: -320px;
|
||||
margin-top: -160px;
|
||||
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
visibility: hidden;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
opacity: 0;
|
||||
background: rgba(255,255,255,0.8);
|
||||
-webkit-transition: all 0.3s;
|
||||
-moz-transition: all 0.3s;
|
||||
-ms-transition: all 0.3s;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.md-show {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.md-show ~ .overlay {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Content styles */
|
||||
.md-content {
|
||||
color: #fff;
|
||||
background: #6b6b6b;
|
||||
position: relative;
|
||||
border-radius: 3px;
|
||||
margin: 0 auto;
|
||||
height: 320px;
|
||||
}
|
||||
|
||||
.md-content h3 {
|
||||
margin: 0;
|
||||
padding: 6px;
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
font-weight: 300;
|
||||
opacity: 0.8;
|
||||
background: rgba(0,0,0,0.1);
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
.md-content > div {
|
||||
padding: 15px 40px 30px;
|
||||
margin: 0;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.md-content > div p {
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.md-content > div ul {
|
||||
margin: 0;
|
||||
padding: 0 0 30px 20px;
|
||||
}
|
||||
|
||||
.md-content > div ul li {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.md-content button {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
/* Effect 1: Fade in and scale up */
|
||||
.md-effect-1 .md-content {
|
||||
-webkit-transform: scale(0.7);
|
||||
-moz-transform: scale(0.7);
|
||||
-ms-transform: scale(0.7);
|
||||
transform: scale(0.7);
|
||||
opacity: 0;
|
||||
-webkit-transition: all 0.3s;
|
||||
-moz-transition: all 0.3s;
|
||||
-ms-transition: all 0.3s;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.md-show.md-effect-1 .md-content {
|
||||
-webkit-transform: scale(1);
|
||||
-moz-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.md-content > .closer {
|
||||
font-size: 18px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
font-size: 24px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1040px) {
|
||||
#viewer{
|
||||
width: 50%;
|
||||
margin-left: 25%;
|
||||
}
|
||||
|
||||
#divider,
|
||||
#divider.show {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 900px) {
|
||||
#viewer{
|
||||
width: 60%;
|
||||
margin-left: 20%;
|
||||
}
|
||||
|
||||
#prev {
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
#next {
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 550px) {
|
||||
#viewer{
|
||||
width: 80%;
|
||||
margin-left: 10%;
|
||||
}
|
||||
|
||||
#prev {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#next {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
height: 100%;
|
||||
top: 45px;
|
||||
width: 10%;
|
||||
text-indent: -10000px;
|
||||
}
|
||||
|
||||
#main {
|
||||
-webkit-transform: translate(0, 0);
|
||||
-moz-transform: translate(0, 0);
|
||||
-ms-transform: translate(0, 0);
|
||||
-webkit-transition: -webkit-transform .3s;
|
||||
-moz-transition: -moz-transform .3s;
|
||||
-ms-transition: -moz-transform .3s;
|
||||
}
|
||||
|
||||
#main.closed {
|
||||
-webkit-transform: translate(260px, 0);
|
||||
-moz-transform: translate(260px, 0);
|
||||
-ms-transform: translate(260px, 0);
|
||||
}
|
||||
|
||||
#titlebar {
|
||||
/* font-size: 16px; */
|
||||
/* margin: 0 50px 0 50px; */
|
||||
}
|
||||
|
||||
#metainfo {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
#tocView {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
#tocView li {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#tocView > ul{
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* For iPad portrait layouts only */
|
||||
@media only screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation: portrait) {
|
||||
#viewer iframe {
|
||||
width: 460px;
|
||||
height: 740px;
|
||||
}
|
||||
}
|
||||
/*For iPad landscape layouts only */
|
||||
@media only screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation: landscape) {
|
||||
#viewer iframe {
|
||||
width: 460px;
|
||||
height: 415px;
|
||||
}
|
||||
}
|
||||
/* For iPhone portrait layouts only */
|
||||
@media only screen and (max-device-width: 480px) and (orientation: portrait) {
|
||||
#viewer {
|
||||
width: 256px;
|
||||
height: 432px;
|
||||
}
|
||||
#viewer iframe {
|
||||
width: 256px;
|
||||
height: 432px;
|
||||
}
|
||||
}
|
||||
/* For iPhone landscape layouts only */
|
||||
@media only screen and (max-device-width: 480px) and (orientation: landscape) {
|
||||
#viewer iframe {
|
||||
width: 256px;
|
||||
height: 124px;
|
||||
}
|
||||
}
|
||||
|
||||
[class^="icon-"]:before, [class*=" icon-"]:before {
|
||||
font-family: "fontello";
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
speak: none;
|
||||
|
||||
display: inline-block;
|
||||
text-decoration: inherit;
|
||||
width: 1em;
|
||||
margin-right: .2em;
|
||||
text-align: center;
|
||||
/* opacity: .8; */
|
||||
|
||||
/* For safety - reset parent styles, that can break glyph codes*/
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
||||
/* you can be more comfortable with increased icons size */
|
||||
font-size: 112%;
|
||||
}
|
||||
|
||||
|
||||
.icon-search:before { content: '\e807'; } /* '' */
|
||||
.icon-resize-full-1:before { content: '\e804'; } /* '' */
|
||||
.icon-cancel-circled2:before { content: '\e80f'; } /* '' */
|
||||
.icon-link:before { content: '\e80d'; } /* '' */
|
||||
.icon-bookmark:before { content: '\e805'; } /* '' */
|
||||
.icon-bookmark-empty:before { content: '\e806'; } /* '' */
|
||||
.icon-download-cloud:before { content: '\e811'; } /* '' */
|
||||
.icon-edit:before { content: '\e814'; } /* '' */
|
||||
.icon-menu:before { content: '\e802'; } /* '' */
|
||||
.icon-cog:before { content: '\e813'; } /* '' */
|
||||
.icon-resize-full:before { content: '\e812'; } /* '' */
|
||||
.icon-cancel-circled:before { content: '\e80e'; } /* '' */
|
||||
.icon-up-dir:before { content: '\e80c'; } /* '' */
|
||||
.icon-right-dir:before { content: '\e80b'; } /* '' */
|
||||
.icon-angle-right:before { content: '\e809'; } /* '' */
|
||||
.icon-angle-down:before { content: '\e80a'; } /* '' */
|
||||
.icon-right:before { content: '\e815'; } /* '' */
|
||||
.icon-list-1:before { content: '\e803'; } /* '' */
|
||||
.icon-list-numbered:before { content: '\e801'; } /* '' */
|
||||
.icon-columns:before { content: '\e810'; } /* '' */
|
||||
.icon-list:before { content: '\e800'; } /* '' */
|
||||
.icon-resize-small:before { content: '\e808'; } /* '' */
|
||||
505
static/epub.js/css/normalize.css
vendored
Executable file
|
|
@ -0,0 +1,505 @@
|
|||
/*! normalize.css v1.0.1 | MIT License | git.io/normalize */
|
||||
|
||||
/* ==========================================================================
|
||||
HTML5 display definitions
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Corrects `block` display not defined in IE 6/7/8/9 and Firefox 3.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Corrects `inline-block` display not defined in IE 6/7/8/9 and Firefox 3.
|
||||
*/
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
video {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevents modern browsers from displaying `audio` without controls.
|
||||
* Remove excess height in iOS 5 devices.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling for `hidden` attribute not present in IE 7/8/9, Firefox 3,
|
||||
* and Safari 4.
|
||||
* Known issue: no IE 6 support.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Base
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* 1. Corrects text resizing oddly in IE 6/7 when body `font-size` is set using
|
||||
* `em` units.
|
||||
* 2. Prevents iOS text size adjust after orientation change, without disabling
|
||||
* user zoom.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-size: 100%; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses `font-family` inconsistency between `textarea` and other form
|
||||
* elements.
|
||||
*/
|
||||
|
||||
html,
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses margins handled incorrectly in IE 6/7.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Links
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Addresses `outline` inconsistency between Chrome and other browsers.
|
||||
*/
|
||||
|
||||
a:focus {
|
||||
outline: thin dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Improves readability when focused and also mouse hovered in all browsers.
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Typography
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Addresses font sizes and margins set differently in IE 6/7.
|
||||
* Addresses font sizes within `section` and `article` in Firefox 4+, Safari 5,
|
||||
* and Chrome.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
margin: 0.83em 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.17em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1em;
|
||||
margin: 1.33em 0;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.83em;
|
||||
margin: 1.67em 0;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 0.75em;
|
||||
margin: 2.33em 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling not present in IE 7/8/9, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling not present in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses styling not present in IE 6/7/8/9.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses margins set differently in IE 6/7.
|
||||
*/
|
||||
|
||||
p,
|
||||
pre {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Corrects font family set oddly in IE 6, Safari 4/5, and Chrome.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, serif;
|
||||
_font-family: 'courier new', monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/*
|
||||
* Improves readability of pre-formatted text in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses CSS quotes not supported in IE 6/7.
|
||||
*/
|
||||
|
||||
q {
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses `quotes` property not supported in Safari 4.
|
||||
*/
|
||||
|
||||
q:before,
|
||||
q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses inconsistent and variable font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Lists
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Addresses margins set differently in IE 6/7.
|
||||
*/
|
||||
|
||||
dl,
|
||||
menu,
|
||||
ol,
|
||||
ul {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin: 0 0 0 40px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses paddings set differently in IE 6/7.
|
||||
*/
|
||||
|
||||
menu,
|
||||
ol,
|
||||
ul {
|
||||
padding: 0 0 0 40px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Corrects list images handled incorrectly in IE 7.
|
||||
*/
|
||||
|
||||
nav ul,
|
||||
nav ol {
|
||||
list-style: none;
|
||||
list-style-image: none;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* 1. Removes border when inside `a` element in IE 6/7/8/9 and Firefox 3.
|
||||
* 2. Improves image quality when scaled in IE 7.
|
||||
*/
|
||||
|
||||
img {
|
||||
border: 0; /* 1 */
|
||||
-ms-interpolation-mode: bicubic; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Corrects overflow displayed oddly in IE 9.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Figures
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Addresses margin not present in IE 6/7/8/9, Safari 5, and Opera 11.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Forms
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Corrects margin displayed oddly in IE 6/7.
|
||||
*/
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Define consistent border, margin, and padding.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Corrects color not being inherited in IE 6/7/8/9.
|
||||
* 2. Corrects text not wrapping in Firefox 3.
|
||||
* 3. Corrects alignment displayed oddly in IE 6/7.
|
||||
*/
|
||||
|
||||
legend {
|
||||
border: 0; /* 1 */
|
||||
padding: 0;
|
||||
white-space: normal; /* 2 */
|
||||
*margin-left: -7px; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Corrects font size not being inherited in all browsers.
|
||||
* 2. Addresses margins set differently in IE 6/7, Firefox 3+, Safari 5,
|
||||
* and Chrome.
|
||||
* 3. Improves appearance and consistency in all browsers.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-size: 100%; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
vertical-align: baseline; /* 3 */
|
||||
*vertical-align: middle; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Addresses Firefox 3+ setting `line-height` on `input` using `!important` in
|
||||
* the UA stylesheet.
|
||||
*/
|
||||
|
||||
button,
|
||||
input {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
||||
* and `video` controls.
|
||||
* 2. Corrects inability to style clickable `input` types in iOS.
|
||||
* 3. Improves usability and consistency of cursor style between image-type
|
||||
* `input` and others.
|
||||
* 4. Removes inner spacing in IE 7 without affecting normal text inputs.
|
||||
* Known issue: inner spacing remains in IE 6.
|
||||
*/
|
||||
|
||||
button,
|
||||
html input[type="button"], /* 1 */
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
cursor: pointer; /* 3 */
|
||||
*overflow: visible; /* 4 */
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-set default cursor for disabled elements.
|
||||
*/
|
||||
|
||||
button[disabled],
|
||||
input[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Addresses box sizing set to content-box in IE 8/9.
|
||||
* 2. Removes excess padding in IE 8/9.
|
||||
* 3. Removes excess padding in IE 7.
|
||||
* Known issue: excess padding remains in IE 6.
|
||||
*/
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
*height: 13px; /* 3 */
|
||||
*width: 13px; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
|
||||
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
|
||||
* (include `-moz` to future-proof).
|
||||
*/
|
||||
/*
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield;
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* Removes inner padding and search cancel button in Safari 5 and Chrome
|
||||
* on OS X.
|
||||
*/
|
||||
|
||||
/* input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
} */
|
||||
|
||||
/*
|
||||
* Removes inner padding and border in Firefox 3+.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Removes default vertical scrollbar in IE 6/7/8/9.
|
||||
* 2. Improves readability and alignment in all browsers.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto; /* 1 */
|
||||
vertical-align: top; /* 2 */
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Tables
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Remove most spacing between table cells.
|
||||
*/
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
96
static/epub.js/css/popup.css
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/* http://davidwalsh.name/css-tooltips */
|
||||
/* base CSS element */
|
||||
.popup {
|
||||
background: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
position: fixed;
|
||||
max-width: 300px;
|
||||
font-size: 12px;
|
||||
|
||||
display: none;
|
||||
margin-left: 2px;
|
||||
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.popup.above {
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
.popup.left {
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
.popup.right {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.pop_content {
|
||||
max-height: 225px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.pop_content > p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* below */
|
||||
.popup:before {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
border-bottom: 10px solid #eee;
|
||||
border-right: 10px solid transparent;
|
||||
border-left: 10px solid transparent;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
left: 50%;
|
||||
top: -10px;
|
||||
margin-left: -6px;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.popup:after {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
border-bottom: 9px solid #eee;
|
||||
border-right: 9px solid transparent;
|
||||
border-left: 9px solid transparent;
|
||||
left: 50%;
|
||||
top: -9px;
|
||||
margin-left: -5px;
|
||||
content: '';
|
||||
}
|
||||
|
||||
/* above */
|
||||
.popup.above:before {
|
||||
border-bottom: none;
|
||||
border-top: 10px solid #eee;
|
||||
border-top-color: rgba(0, 0, 0, 0.2);
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.popup.above:after {
|
||||
border-bottom: none;
|
||||
border-top: 9px solid #eee;
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.popup.left:before,
|
||||
.popup.left:after
|
||||
{
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
.popup.right:before,
|
||||
.popup.right:after
|
||||
{
|
||||
left: auto;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
|
||||
.popup.show, .popup.on {
|
||||
display: block;
|
||||
}
|
||||
BIN
static/epub.js/font/fontello.eot
Normal file
33
static/epub.js/font/fontello.svg
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Copyright (C) 2013 by original authors @ fontello.com</metadata>
|
||||
<defs>
|
||||
<font id="fontello" horiz-adv-x="1000" >
|
||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||
<missing-glyph horiz-adv-x="1000" />
|
||||
<glyph glyph-name="search" unicode="" d="m643 386q0 103-74 176t-176 74t-177-74t-73-176t73-177t177-73t176 73t74 177z m286-465q0-29-22-50t-50-21q-30 0-50 21l-191 191q-100-69-223-69q-80 0-153 31t-125 84t-84 125t-31 153t31 152t84 126t125 84t153 31t152-31t126-84t84-126t31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
|
||||
<glyph glyph-name="resize-full-1" unicode="" d="m784 111l127 128l0-336l-335 0l128 130l-128 127l79 79z m-431 686l-129-127l128-127l-80-80l-126 128l-128-129l0 335l335 0z m0-637l-129-127l129-130l-335 0l0 336l128-128l128 128z m558 637l0-335l-127 129l-128-128l-79 80l127 127l-128 127l335 0z" horiz-adv-x="928" />
|
||||
<glyph glyph-name="cancel-circled2" unicode="" d="m612 248l-81-82q-6-5-13-5t-13 5l-76 77l-77-77q-5-5-13-5t-13 5l-81 82q-6 5-6 13t6 13l76 76l-76 76q-6 6-6 13t6 13l81 82q6 5 13 5t13-5l77-77l76 77q6 5 13 5t13-5l81-82q6-5 6-13t-6-13l-76-76l76-76q6-6 6-13t-6-13z m120 102q0 83-41 152t-110 111t-152 41t-153-41t-110-111t-41-152t41-152t110-111t153-41t152 41t110 111t41 152z m125 0q0-117-57-215t-156-156t-215-58t-216 58t-155 156t-58 215t58 215t155 156t216 58t215-58t156-156t57-215z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="link" unicode="" d="m812 171q0 23-15 38l-116 116q-16 16-38 16q-24 0-40-18q1-1 10-10t12-12t9-11t7-14t2-15q0-23-16-38t-38-16q-8 0-15 2t-14 7t-11 9t-12 12t-10 10q-19-17-19-40q0-23 16-38l115-116q15-15 38-15q22 0 38 15l82 81q15 16 15 37z m-392 394q0 22-15 38l-115 115q-16 16-38 16q-22 0-38-15l-82-82q-16-15-16-37q0-22 16-38l116-116q15-15 38-15q23 0 40 17q-2 2-11 11t-12 12t-8 10t-7 14t-2 16q0 22 15 38t38 15q9 0 16-2t14-7t10-8t12-12t11-11q18 17 18 41z m500-394q0-67-48-113l-82-81q-46-47-113-47q-68 0-114 48l-115 115q-46 47-46 114q0 68 49 116l-49 49q-48-49-116-49q-67 0-114 47l-116 116q-47 47-47 114t47 113l82 82q47 46 114 46q67 0 114-47l114-116q47-46 47-113q0-69-49-117l49-49q48 49 116 49q67 0 114-47l116-116q47-47 47-114z" horiz-adv-x="928.6" />
|
||||
<glyph glyph-name="bookmark" unicode="" d="m650 779q12 0 24-5q19-8 29-23t11-35v-719q0-19-11-35t-29-23q-10-4-24-4q-27 0-47 18l-246 236l-246-236q-20-19-46-19q-13 0-25 5q-18 7-29 23t-11 35v719q0 19 11 35t29 23q12 5 25 5h585z" horiz-adv-x="714.3" />
|
||||
<glyph glyph-name="bookmark-empty" unicode="" d="m643 707h-572v-693l237 227l49 47l50-47l236-227v693z m7 72q12 0 24-5q19-8 29-23t11-35v-719q0-19-11-35t-29-23q-10-4-24-4q-27 0-47 18l-246 236l-246-236q-20-19-46-19q-13 0-25 5q-18 7-29 23t-11 35v719q0 19 11 35t29 23q12 5 25 5h585z" horiz-adv-x="714.3" />
|
||||
<glyph glyph-name="download-cloud" unicode="" d="m714 332q0 8-5 13t-13 5h-125v196q0 8-5 13t-12 5h-108q-7 0-12-5t-5-13v-196h-125q-8 0-13-5t-5-13q0-8 5-13l196-196q5-5 13-5t13 5l196 196q5 6 5 13z m357-125q0-89-62-151t-152-63h-607q-103 0-177 73t-73 177q0 72 39 134t105 92q-1 17-1 24q0 118 84 202t202 84q87 0 159-49t105-129q40 35 93 35q59 0 101-42t42-101q0-43-23-77q72-17 119-76t46-133z" horiz-adv-x="1071.4" />
|
||||
<glyph glyph-name="edit" unicode="" d="m496 189l64 65l-85 85l-64-65v-31h53v-54h32z m245 402q-9 9-18 0l-196-196q-9-9 0-18t18 0l196 196q9 9 0 18z m45-331v-106q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h464q35 0 65-14q9-4 10-13q2-10-5-16l-27-28q-8-8-18-4q-13 3-25 3h-464q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v70q0 7 5 12l36 36q8 8 20 4t11-16z m-54 411l161-160l-375-375h-161v160z m248-73l-51-52l-161 161l51 51q16 16 38 16t38-16l85-84q16-16 16-38t-16-38z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="menu" unicode="" d="m857 100v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 25t25 11h785q15 0 26-11t10-25z m0 286v-72q0-14-10-25t-26-10h-785q-15 0-25 10t-11 25v72q0 14 11 25t25 10h785q15 0 26-10t10-25z m0 285v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 26t25 10h785q15 0 26-10t10-26z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="cog" unicode="" d="m571 350q0 59-41 101t-101 42t-101-42t-42-101t42-101t101-42t101 42t41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51q19-27 59-77q6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21q-9-76-16-104q-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5q-8 0-14 6q-70 64-92 94q-4 5-4 13q0 6 5 12q8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51q-23 32-60 77q-6 7-6 14q0 5 5 12q15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21q9 76 17 104q3 15 20 15h124q7 0 13-4t7-12l15-103q28-9 50-21l80 60q5 5 13 5q7 0 14-5q72-67 92-95q4-5 4-13q0-6-4-12q-9-12-29-38t-30-39q14-28 23-55l102-15q7-1 12-7t4-13z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="resize-full" unicode="" d="m421 261q0-8-5-13l-185-185l80-81q10-10 10-25t-10-25t-25-11h-250q-15 0-25 11t-11 25v250q0 15 11 25t25 11t25-11l80-80l185 185q6 6 13 6t13-6l64-63q5-6 5-13z m436 482v-250q0-15-10-25t-26-11t-25 11l-80 80l-185-185q-6-6-13-6t-13 6l-64 63q-5 6-5 13t5 13l186 185l-81 81q-10 10-10 25t10 25t25 11h250q15 0 26-11t10-25z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="cancel-circled" unicode="" d="m641 224q0 14-10 25l-101 101l101 101q10 11 10 25q0 15-10 26l-51 50q-10 11-25 11q-15 0-25-11l-101-101l-101 101q-11 11-26 11q-15 0-25-11l-50-50q-11-11-11-26q0-14 11-25l101-101l-101-101q-11-11-11-25q0-15 11-26l50-50q10-11 25-11q15 0 26 11l101 101l101-101q10-11 25-11q15 0 25 11l51 50q10 11 10 26z m216 126q0-117-57-215t-156-156t-215-58t-216 58t-155 156t-58 215t58 215t155 156t216 58t215-58t156-156t57-215z" horiz-adv-x="857.1" />
|
||||
<glyph glyph-name="up-dir" unicode="" d="m571 171q0-14-10-25t-25-10h-500q-15 0-25 10t-11 25t11 26l250 250q10 10 25 10t25-10l250-250q10-11 10-26z" horiz-adv-x="571.4" />
|
||||
<glyph glyph-name="right-dir" unicode="" d="m321 350q0-14-10-25l-250-250q-11-11-25-11t-25 11t-11 25v500q0 15 11 25t25 11t25-11l250-250q10-10 10-25z" horiz-adv-x="357.1" />
|
||||
<glyph glyph-name="angle-right" unicode="" d="m332 314q0-7-6-13l-260-260q-5-5-12-5t-13 5l-28 28q-6 6-6 13t6 13l219 219l-219 220q-6 5-6 12t6 13l28 28q5 6 13 6t12-6l260-260q6-5 6-13z" horiz-adv-x="357.1" />
|
||||
<glyph glyph-name="angle-down" unicode="" d="m600 439q0-7-6-13l-260-260q-5-5-13-5t-12 5l-260 260q-6 6-6 13t6 13l27 28q6 6 13 6t13-6l219-219l220 219q5 6 13 6t12-6l28-28q6-5 6-13z" horiz-adv-x="642.9" />
|
||||
<glyph glyph-name="right" unicode="" d="m1000 404v-108q0-7-5-12t-13-5h-696v-125q0-12-11-17t-19 3l-215 196q-5 5-5 12q0 8 5 14l215 197q9 8 19 4q11-5 11-17v-125h696q8 0 13-5t5-12z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="list-1" unicode="" d="m143 118v-107q0-7-5-13t-13-5h-107q-7 0-13 5t-5 13v107q0 7 5 12t13 6h107q7 0 13-6t5-12z m0 214v-107q0-7-5-13t-13-5h-107q-7 0-13 5t-5 13v107q0 7 5 13t13 5h107q7 0 13-5t5-13z m0 214v-107q0-7-5-12t-13-6h-107q-7 0-13 6t-5 12v107q0 8 5 13t13 5h107q7 0 13-5t5-13z m857-428v-107q0-7-5-13t-13-5h-750q-7 0-12 5t-6 13v107q0 7 6 12t12 6h750q7 0 13-6t5-12z m-857 643v-107q0-8-5-13t-13-5h-107q-7 0-13 5t-5 13v107q0 7 5 12t13 6h107q7 0 13-6t5-12z m857-429v-107q0-7-5-13t-13-5h-750q-7 0-12 5t-6 13v107q0 7 6 13t12 5h750q7 0 13-5t5-13z m0 214v-107q0-7-5-12t-13-6h-750q-7 0-12 6t-6 12v107q0 8 6 13t12 5h750q7 0 13-5t5-13z m0 215v-107q0-8-5-13t-13-5h-750q-7 0-12 5t-6 13v107q0 7 6 12t12 6h750q7 0 13-6t5-12z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="list-numbered" unicode="" d="m213-54q0-45-31-70t-75-26q-60 0-96 37l31 49q28-25 60-25q16 0 28 8t12 24q0 35-59 31l-14 31q4 6 18 24t24 31t20 21v1q-9 0-27-1t-27 0v-30h-59v85h186v-49l-53-65q28-6 45-27t17-49z m1 350v-89h-202q-4 20-4 30q0 29 14 52t31 38t37 27t31 24t14 25q0 14-9 22t-22 7q-25 0-45-32l-47 33q13 28 40 44t59 16q40 0 68-23t28-63q0-28-19-51t-42-36t-42-28t-20-30h71v34h59z m786-178v-107q0-8-5-13t-13-5h-678q-8 0-13 5t-5 13v107q0 8 5 13t13 5h678q7 0 13-6t5-12z m-786 502v-56h-187v56h60q0 22 0 68t1 67v7h-1q-5-10-28-30l-40 42l76 71h59v-225h60z m786-216v-108q0-7-5-12t-13-5h-678q-8 0-13 5t-5 12v108q0 7 5 12t13 5h678q7 0 13-5t5-12z m0 285v-107q0-7-5-12t-13-6h-678q-8 0-13 6t-5 12v107q0 8 5 13t13 5h678q7 0 13-5t5-13z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="columns" unicode="" d="m89-7h340v643h-358v-625q0-8 6-13t12-5z m768 18v625h-357v-643h339q8 0 13 5t5 13z m72 678v-678q0-37-27-63t-63-27h-750q-36 0-63 27t-26 63v678q0 37 26 63t63 27h750q37 0 63-27t27-63z" horiz-adv-x="928.6" />
|
||||
<glyph glyph-name="list" unicode="" d="m100 200q20 0 35-15t15-35t-15-35t-35-15l-50 0q-20 0-35 15t-15 35t14 35t36 15l50 0z m0 200q20 0 35-15t15-35t-15-35t-35-15l-50 0q-20 0-35 15t-15 35t14 35t36 15l50 0z m0 200q20 0 35-15t15-35t-15-35t-35-15l-50 0q-20 0-35 15t-15 35t14 35t36 15l50 0z m200-100q-20 0-35 15t-15 35t15 35t35 15l350 0q22 0 36-15t14-35t-15-35t-35-15l-350 0z m350-100q22 0 36-15t14-35t-15-35t-35-15l-350 0q-20 0-35 15t-15 35t15 35t35 15l350 0z m0-200q22 0 36-15t14-35t-15-35t-35-15l-350 0q-20 0-35 15t-15 35t15 35t35 15l350 0z" horiz-adv-x="700" />
|
||||
<glyph glyph-name="resize-small" unicode="" d="m429 314v-250q0-14-11-25t-25-10t-25 10l-81 81l-185-186q-5-5-13-5t-13 5l-63 64q-6 5-6 13t6 13l185 185l-80 80q-11 11-11 25t11 25t25 11h250q14 0 25-11t11-25z m421 375q0-7-6-13l-185-185l80-80q11-11 11-25t-11-25t-25-11h-250q-14 0-25 11t-10 25v250q0 14 10 25t25 10t25-10l81-81l185 186q6 5 13 5t13-5l63-64q6-5 6-13z" horiz-adv-x="857.1" />
|
||||
</font>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.4 KiB |
BIN
static/epub.js/font/fontello.ttf
Normal file
BIN
static/epub.js/font/fontello.woff
Normal file
0
static/epub.js/img/.gitignore
vendored
Executable file
BIN
static/epub.js/img/annotator-glyph-sprite.png
Normal file
|
After Width: | Height: | Size: 5 KiB |
BIN
static/epub.js/img/annotator-icon-sprite.png
Normal file
|
After Width: | Height: | Size: 7 KiB |
BIN
static/epub.js/img/apple-touch-icon.png
Executable file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
static/epub.js/img/cancelfullscreen.png
Normal file
|
After Width: | Height: | Size: 246 B |
BIN
static/epub.js/img/close.png
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
static/epub.js/img/fullscreen.png
Normal file
|
After Width: | Height: | Size: 220 B |
BIN
static/epub.js/img/loader.gif
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
static/epub.js/img/menu-icon.png
Normal file
|
After Width: | Height: | Size: 947 B |
BIN
static/epub.js/img/save.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
static/epub.js/img/saved.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
static/epub.js/img/settings-s.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
static/epub.js/img/settings.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
static/epub.js/img/star.png
Normal file
|
After Width: | Height: | Size: 278 B |
1
static/epub.js/index.html
Executable file
|
|
@ -0,0 +1 @@
|
|||
|
||||
23139
static/epub.js/js/epub.js
Normal file
1
static/epub.js/js/epub.min.js
vendored
Normal file
1
static/epub.js/js/epub.min.map
Normal file
1
static/epub.js/js/hooks.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
EPUBJS.Hooks.register("beforeChapterDisplay").endnotes=function(a,b){var c=b.contents.querySelectorAll("a[href]"),d=Array.prototype.slice.call(c),e=EPUBJS.core.folder(location.pathname),f=(EPUBJS.cssPath,{});EPUBJS.core.addCss(EPUBJS.cssPath+"popup.css",!1,b.render.document.head),d.forEach(function(a){function c(){var c,h,n=b.height,o=b.width,p=225;m||(c=j.cloneNode(!0),m=c.querySelector("p")),f[i]||(f[i]=document.createElement("div"),f[i].setAttribute("class","popup"),pop_content=document.createElement("div"),f[i].appendChild(pop_content),pop_content.appendChild(m),pop_content.setAttribute("class","pop_content"),b.render.document.body.appendChild(f[i]),f[i].addEventListener("mouseover",d,!1),f[i].addEventListener("mouseout",e,!1),b.on("renderer:pageChanged",g,this),b.on("renderer:pageChanged",e,this)),c=f[i],h=a.getBoundingClientRect(),k=h.left,l=h.top,c.classList.add("show"),popRect=c.getBoundingClientRect(),c.style.left=k-popRect.width/2+"px",c.style.top=l+"px",p>n/2.5&&(p=n/2.5,pop_content.style.maxHeight=p+"px"),popRect.height+l>=n-25?(c.style.top=l-popRect.height+"px",c.classList.add("above")):c.classList.remove("above"),k-popRect.width<=0?(c.style.left=k+"px",c.classList.add("left")):c.classList.remove("left"),k+popRect.width/2>=o?(c.style.left=k-300+"px",popRect=c.getBoundingClientRect(),c.style.left=k-popRect.width+"px",popRect.height+l>=n-25?(c.style.top=l-popRect.height+"px",c.classList.add("above")):c.classList.remove("above"),c.classList.add("right")):c.classList.remove("right")}function d(){f[i].classList.add("on")}function e(){f[i].classList.remove("on")}function g(){setTimeout(function(){f[i].classList.remove("show")},100)}var h,i,j,k,l,m;"noteref"==a.getAttribute("epub:type")&&(h=a.getAttribute("href"),i=h.replace("#",""),j=b.render.document.getElementById(i),a.addEventListener("mouseover",c,!1),a.addEventListener("mouseout",g,!1))}),a&&a()},EPUBJS.Hooks.register("beforeChapterDisplay").mathml=function(a,b){if(b.currentChapter.manifestProperties.indexOf("mathml")!==-1){b.render.iframe.contentWindow.mathmlCallback=a;var c=document.createElement("script");c.type="text/x-mathjax-config",c.innerHTML=' MathJax.Hub.Register.StartupHook("End",function () { window.mathmlCallback(); }); MathJax.Hub.Config({jax: ["input/TeX","input/MathML","output/SVG"],extensions: ["tex2jax.js","mml2jax.js","MathEvents.js"],TeX: {extensions: ["noErrors.js","noUndefined.js","autoload-all.js"]},MathMenu: {showRenderer: false},menuSettings: {zoom: "Click"},messageStyle: "none"}); ',b.doc.body.appendChild(c),EPUBJS.core.addScript("http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML",null,b.doc.head)}else a&&a()},EPUBJS.Hooks.register("beforeChapterDisplay").smartimages=function(a,b){var c=b.contents.querySelectorAll("img"),d=Array.prototype.slice.call(c),e=b.height;if("reflowable"!=b.layoutSettings.layout)return void a();d.forEach(function(a){var c=function(){var c,d=a.getBoundingClientRect(),f=d.height,g=d.top,h=a.getAttribute("data-height"),i=h||f,j=Number(getComputedStyle(a,"").fontSize.match(/(\d*(\.\d*)?)px/)[1]),k=j?j/2:0;e=b.contents.clientHeight,g<0&&(g=0),a.style.maxWidth="100%",i+g>=e?(g<e/2?(c=e-g-k,a.style.maxHeight=c+"px",a.style.width="auto"):(i>e&&(a.style.maxHeight=e+"px",a.style.width="auto",d=a.getBoundingClientRect(),i=d.height),a.style.display="block",a.style.WebkitColumnBreakBefore="always",a.style.breakBefore="column"),a.setAttribute("data-height",c)):(a.style.removeProperty("max-height"),a.style.removeProperty("margin-top"))},d=function(){b.off("renderer:resized",c),b.off("renderer:chapterUnload",this)};a.addEventListener("load",c,!1),b.on("renderer:resized",c),b.on("renderer:chapterUnload",d),c()}),a&&a()},EPUBJS.Hooks.register("beforeChapterDisplay").transculsions=function(a,b){var c=b.contents.querySelectorAll("[transclusion]");Array.prototype.slice.call(c).forEach(function(a){function c(){j=g,k=h,j>chapter.colWidth&&(d=chapter.colWidth/j,j=chapter.colWidth,k*=d),f.width=j,f.height=k}var d,e=a.getAttribute("ref"),f=document.createElement("iframe"),g=a.getAttribute("width"),h=a.getAttribute("height"),i=a.parentNode,j=g,k=h;c(),b.listenUntil("renderer:resized","renderer:chapterUnloaded",c),f.src=e,i.replaceChild(f,a)}),a&&a()};
|
||||
1
static/epub.js/js/hooks.min.map
Normal file
14
static/epub.js/js/hooks/extensions/highlight.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
EPUBJS.Hooks.register("beforeChapterDisplay").highlight = function(callback, renderer){
|
||||
|
||||
// EPUBJS.core.addScript("js/libs/jquery.highlight.js", null, renderer.doc.head);
|
||||
|
||||
var s = document.createElement("style");
|
||||
s.innerHTML =".highlight { background: yellow; font-weight: normal; }";
|
||||
|
||||
renderer.render.document.head.appendChild(s);
|
||||
|
||||
if(callback) callback();
|
||||
|
||||
}
|
||||
|
||||
|
||||
4
static/epub.js/js/libs/jquery.min.js
vendored
Normal file
7
static/epub.js/js/libs/localforage.min.js
vendored
Normal file
145
static/epub.js/js/libs/screenfull.js
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/*!
|
||||
* screenfull
|
||||
* v2.0.0 - 2014-12-22
|
||||
* (c) Sindre Sorhus; MIT License
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var isCommonjs = typeof module !== 'undefined' && module.exports;
|
||||
var keyboardAllowed = typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element;
|
||||
|
||||
var fn = (function () {
|
||||
var val;
|
||||
var valLength;
|
||||
|
||||
var fnMap = [
|
||||
[
|
||||
'requestFullscreen',
|
||||
'exitFullscreen',
|
||||
'fullscreenElement',
|
||||
'fullscreenEnabled',
|
||||
'fullscreenchange',
|
||||
'fullscreenerror'
|
||||
],
|
||||
// new WebKit
|
||||
[
|
||||
'webkitRequestFullscreen',
|
||||
'webkitExitFullscreen',
|
||||
'webkitFullscreenElement',
|
||||
'webkitFullscreenEnabled',
|
||||
'webkitfullscreenchange',
|
||||
'webkitfullscreenerror'
|
||||
|
||||
],
|
||||
// old WebKit (Safari 5.1)
|
||||
[
|
||||
'webkitRequestFullScreen',
|
||||
'webkitCancelFullScreen',
|
||||
'webkitCurrentFullScreenElement',
|
||||
'webkitCancelFullScreen',
|
||||
'webkitfullscreenchange',
|
||||
'webkitfullscreenerror'
|
||||
|
||||
],
|
||||
[
|
||||
'mozRequestFullScreen',
|
||||
'mozCancelFullScreen',
|
||||
'mozFullScreenElement',
|
||||
'mozFullScreenEnabled',
|
||||
'mozfullscreenchange',
|
||||
'mozfullscreenerror'
|
||||
],
|
||||
[
|
||||
'msRequestFullscreen',
|
||||
'msExitFullscreen',
|
||||
'msFullscreenElement',
|
||||
'msFullscreenEnabled',
|
||||
'MSFullscreenChange',
|
||||
'MSFullscreenError'
|
||||
]
|
||||
];
|
||||
|
||||
var i = 0;
|
||||
var l = fnMap.length;
|
||||
var ret = {};
|
||||
|
||||
for (; i < l; i++) {
|
||||
val = fnMap[i];
|
||||
if (val && val[1] in document) {
|
||||
for (i = 0, valLength = val.length; i < valLength; i++) {
|
||||
ret[fnMap[0][i]] = val[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
})();
|
||||
|
||||
var screenfull = {
|
||||
request: function (elem) {
|
||||
var request = fn.requestFullscreen;
|
||||
|
||||
elem = elem || document.documentElement;
|
||||
|
||||
// Work around Safari 5.1 bug: reports support for
|
||||
// keyboard in fullscreen even though it doesn't.
|
||||
// Browser sniffing, since the alternative with
|
||||
// setTimeout is even worse.
|
||||
if (/5\.1[\.\d]* Safari/.test(navigator.userAgent)) {
|
||||
elem[request]();
|
||||
} else {
|
||||
elem[request](keyboardAllowed && Element.ALLOW_KEYBOARD_INPUT);
|
||||
}
|
||||
},
|
||||
exit: function () {
|
||||
document[fn.exitFullscreen]();
|
||||
},
|
||||
toggle: function (elem) {
|
||||
if (this.isFullscreen) {
|
||||
this.exit();
|
||||
} else {
|
||||
this.request(elem);
|
||||
}
|
||||
},
|
||||
raw: fn
|
||||
};
|
||||
|
||||
if (!fn) {
|
||||
if (isCommonjs) {
|
||||
module.exports = false;
|
||||
} else {
|
||||
window.screenfull = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Object.defineProperties(screenfull, {
|
||||
isFullscreen: {
|
||||
get: function () {
|
||||
return !!document[fn.fullscreenElement];
|
||||
}
|
||||
},
|
||||
element: {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return document[fn.fullscreenElement];
|
||||
}
|
||||
},
|
||||
enabled: {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
// Coerce to boolean in case of old WebKit
|
||||
return !!document[fn.fullscreenEnabled];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (isCommonjs) {
|
||||
module.exports = screenfull;
|
||||
} else {
|
||||
window.screenfull = screenfull;
|
||||
}
|
||||
})();
|
||||
7
static/epub.js/js/libs/screenfull.min.js
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
* screenfull
|
||||
* v1.1.0 - 2013-09-06
|
||||
* https://github.com/sindresorhus/screenfull.js
|
||||
* (c) Sindre Sorhus; MIT License
|
||||
*/
|
||||
!function(a,b){"use strict";var c="undefined"!=typeof Element&&"ALLOW_KEYBOARD_INPUT"in Element,d=function(){for(var a,c,d=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror"],["webkitRequestFullScreen","webkitCancelFullScreen","webkitCurrentFullScreenElement","webkitCancelFullScreen","webkitfullscreenchange","webkitfullscreenerror"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenchange","MSFullscreenerror"]],e=0,f=d.length,g={};f>e;e++)if(a=d[e],a&&a[1]in b){for(e=0,c=a.length;c>e;e++)g[d[0][e]]=a[e];return g}return!1}(),e={request:function(a){var e=d.requestFullscreen;a=a||b.documentElement,/5\.1[\.\d]* Safari/.test(navigator.userAgent)?a[e]():a[e](c&&Element.ALLOW_KEYBOARD_INPUT)},exit:function(){b[d.exitFullscreen]()},toggle:function(a){this.isFullscreen?this.exit():this.request(a)},onchange:function(){},onerror:function(){},raw:d};return d?(Object.defineProperties(e,{isFullscreen:{get:function(){return!!b[d.fullscreenElement]}},element:{enumerable:!0,get:function(){return b[d.fullscreenElement]}},enabled:{enumerable:!0,get:function(){return!!b[d.fullscreenEnabled]}}}),b.addEventListener(d.fullscreenchange,function(a){e.onchange.call(e,a)}),b.addEventListener(d.fullscreenerror,function(a){e.onerror.call(e,a)}),a.screenfull=e,void 0):(a.screenfull=!1,void 0)}(window,document);
|
||||
15
static/epub.js/js/libs/zip.min.js
vendored
Normal file
80
static/epub.js/js/plugins/hypothesis.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// Hypothesis Customized embedding
|
||||
// This hypothesis config function returns a new constructor which modifies
|
||||
// annotator for a better integration. Below we create our own EpubAnnotationSidebar
|
||||
// Constructor, customizing the show and hide function to take acount for the reader UI.
|
||||
|
||||
window.hypothesisConfig = function() {
|
||||
var Annotator = window.Annotator;
|
||||
var $main = $("#main");
|
||||
|
||||
function EpubAnnotationSidebar(elem, options) {
|
||||
options = {
|
||||
server: true,
|
||||
origin: true,
|
||||
showHighlights: true,
|
||||
Toolbar: {container: '#annotation-controls'}
|
||||
}
|
||||
|
||||
Annotator.Host.call(this, elem, options);
|
||||
}
|
||||
|
||||
EpubAnnotationSidebar.prototype = Object.create(Annotator.Host.prototype);
|
||||
|
||||
EpubAnnotationSidebar.prototype.show = function() {
|
||||
this.frame.css({
|
||||
'margin-left': (-1 * this.frame.width()) + "px"
|
||||
});
|
||||
this.frame.removeClass('annotator-collapsed');
|
||||
if (!$main.hasClass('single')) {
|
||||
$main.addClass("single");
|
||||
this.toolbar.find('[name=sidebar-toggle]').removeClass('h-icon-chevron-left').addClass('h-icon-chevron-right');
|
||||
this.setVisibleHighlights(true);
|
||||
}
|
||||
};
|
||||
|
||||
EpubAnnotationSidebar.prototype.hide = function() {
|
||||
this.frame.css({
|
||||
'margin-left': ''
|
||||
});
|
||||
this.frame.addClass('annotator-collapsed');
|
||||
if ($main.hasClass('single')) {
|
||||
$main.removeClass("single");
|
||||
this.toolbar.find('[name=sidebar-toggle]').removeClass('h-icon-chevron-right').addClass('h-icon-chevron-left');
|
||||
this.setVisibleHighlights(false);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
constructor: EpubAnnotationSidebar,
|
||||
}
|
||||
};
|
||||
|
||||
// This is the Epub.js plugin. Annotations are updated on location change.
|
||||
EPUBJS.reader.plugins.HypothesisController = function (Book) {
|
||||
var reader = this;
|
||||
var $main = $("#main");
|
||||
|
||||
var updateAnnotations = function () {
|
||||
var annotator = Book.renderer.render.window.annotator;
|
||||
if (annotator && annotator.constructor.$) {
|
||||
var annotations = getVisibleAnnotations(annotator.constructor.$);
|
||||
annotator.showAnnotations(annotations)
|
||||
}
|
||||
};
|
||||
|
||||
var getVisibleAnnotations = function ($) {
|
||||
var width = Book.renderer.render.iframe.clientWidth;
|
||||
return $('.annotator-hl').map(function() {
|
||||
var $this = $(this),
|
||||
left = this.getBoundingClientRect().left;
|
||||
|
||||
if (left >= 0 && left <= width) {
|
||||
return $this.data('annotation');
|
||||
}
|
||||
}).get();
|
||||
};
|
||||
|
||||
Book.on("renderer:locationChanged", updateAnnotations);
|
||||
|
||||
return {}
|
||||
};
|
||||
125
static/epub.js/js/plugins/search.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
EPUBJS.reader.search = {};
|
||||
|
||||
// Search Server -- https://github.com/futurepress/epubjs-search
|
||||
EPUBJS.reader.search.SERVER = "https://pacific-cliffs-3579.herokuapp.com";
|
||||
|
||||
EPUBJS.reader.search.request = function(q, callback) {
|
||||
var fetch = $.ajax({
|
||||
dataType: "json",
|
||||
url: EPUBJS.reader.search.SERVER + "/search?q=" + encodeURIComponent(q)
|
||||
});
|
||||
|
||||
fetch.fail(function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
fetch.done(function(results) {
|
||||
callback(results);
|
||||
});
|
||||
};
|
||||
|
||||
EPUBJS.reader.plugins.SearchController = function(Book) {
|
||||
var reader = this;
|
||||
|
||||
var $searchBox = $("#searchBox"),
|
||||
$searchResults = $("#searchResults"),
|
||||
$searchView = $("#searchView"),
|
||||
iframeDoc;
|
||||
|
||||
var searchShown = false;
|
||||
|
||||
var onShow = function() {
|
||||
query();
|
||||
searchShown = true;
|
||||
$searchView.addClass("shown");
|
||||
};
|
||||
|
||||
var onHide = function() {
|
||||
searchShown = false;
|
||||
$searchView.removeClass("shown");
|
||||
};
|
||||
|
||||
var query = function() {
|
||||
var q = $searchBox.val();
|
||||
|
||||
if(q == '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$searchResults.empty();
|
||||
$searchResults.append("<li><p>Searching...</p></li>");
|
||||
|
||||
|
||||
|
||||
EPUBJS.reader.search.request(q, function(data) {
|
||||
var results = data.results;
|
||||
|
||||
$searchResults.empty();
|
||||
|
||||
if(iframeDoc) {
|
||||
$(iframeDoc).find('body').unhighlight();
|
||||
}
|
||||
|
||||
if(results.length == 0) {
|
||||
$searchResults.append("<li><p>No Results Found</p></li>");
|
||||
return;
|
||||
}
|
||||
|
||||
iframeDoc = $("#viewer iframe")[0].contentDocument;
|
||||
$(iframeDoc).find('body').highlight(q, { element: 'span' });
|
||||
|
||||
results.forEach(function(result) {
|
||||
var $li = $("<li></li>");
|
||||
var $item = $("<a href='"+result.href+"' data-cfi='"+result.cfi+"'><span>"+result.title+"</span><p>"+result.highlight+"</p></a>");
|
||||
|
||||
$item.on("click", function(e) {
|
||||
var $this = $(this),
|
||||
cfi = $this.data("cfi");
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
Book.gotoCfi(cfi+"/1:0");
|
||||
|
||||
Book.on("renderer:chapterDisplayed", function() {
|
||||
iframeDoc = $("#viewer iframe")[0].contentDocument;
|
||||
$(iframeDoc).find('body').highlight(q, { element: 'span' });
|
||||
})
|
||||
|
||||
|
||||
|
||||
});
|
||||
$li.append($item);
|
||||
$searchResults.append($li);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$searchBox.on("search", function(e) {
|
||||
var q = $searchBox.val();
|
||||
|
||||
//-- SearchBox is empty or cleared
|
||||
if(q == '') {
|
||||
$searchResults.empty();
|
||||
if(reader.SidebarController.getActivePanel() == "Search") {
|
||||
reader.SidebarController.changePanelTo("Toc");
|
||||
}
|
||||
|
||||
$(iframeDoc).find('body').unhighlight();
|
||||
iframeDoc = false;
|
||||
return;
|
||||
}
|
||||
|
||||
reader.SidebarController.changePanelTo("Search");
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
|
||||
return {
|
||||
"show" : onShow,
|
||||
"hide" : onHide
|
||||
};
|
||||
};
|
||||
4372
static/epub.js/js/reader.js
Normal file
89
static/epub.js/js/reader.js.map
Normal file
8
static/epub.js/js/reader.min.js
vendored
Normal file
1
static/epub.js/js/reader.min.map
Normal file
99
static/js/EpubViewer.js
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
'use strict';
|
||||
|
||||
/*@
|
||||
Ox.EpubViewer <f> Epub Viewer
|
||||
options <o> Options
|
||||
center <[n]|s|'auto'> Center ([x, y] or 'auto')
|
||||
height <n|384> Viewer height in px
|
||||
maxZoom <n|16> Maximum zoom (minimum zoom is 'fit')
|
||||
epubjsURL <s|'/static/epub.js/'> URL to epub.js
|
||||
url <s|''> Epub URL
|
||||
width <n|512> Viewer width in px
|
||||
zoom <n|s|'fit'> Zoom (number or 'fit' or 'fill')
|
||||
self <o> Shared private variable
|
||||
([options[, self]]) -> <o:OxElement> Epub Viewer
|
||||
center <!> Center changed
|
||||
center <[n]|s> Center
|
||||
zoom <!> Zoom changed
|
||||
zoom <n|s> Zoom
|
||||
page <!> Page changed
|
||||
page <n|s> Page
|
||||
@*/
|
||||
Ox.EpubViewer = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = Ox.Element({}, self)
|
||||
.defaults({
|
||||
center: 'auto',
|
||||
height: 384,
|
||||
page: 1,
|
||||
maxZoom: 16,
|
||||
url: '',
|
||||
width: 512,
|
||||
zoom: 'fit'
|
||||
})
|
||||
.options(options || {})
|
||||
.update({
|
||||
center: function() {
|
||||
setCenterAndZoom();
|
||||
},
|
||||
page: updatePage,
|
||||
// allow for setting height and width at the same time
|
||||
height: updateSize,
|
||||
url: function() {
|
||||
self.$iframe.postMessage('epub', {epub: self.options.url});
|
||||
},
|
||||
width: updateSize,
|
||||
zoom: function() {
|
||||
setCenterAndZoom();
|
||||
}
|
||||
})
|
||||
.addClass('OxEpubViewer')
|
||||
.on({
|
||||
})
|
||||
.bindEvent({
|
||||
});
|
||||
|
||||
self.$iframe = Ox.Element('<iframe>')
|
||||
.attr({
|
||||
frameborder: 0,
|
||||
height: self.options.height + 'px',
|
||||
src: self.options.url,
|
||||
width: self.options.width + 'px'
|
||||
})
|
||||
.onMessage(function(data, event) {
|
||||
that.triggerEvent(event, data);
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
updateSize();
|
||||
|
||||
function setCenterAndZoom() {
|
||||
}
|
||||
|
||||
function updatePage() {
|
||||
self.$iframe.postMessage('page', {page: self.options.page});
|
||||
}
|
||||
|
||||
function updateSize() {
|
||||
that.css({
|
||||
height: self.options.height + 'px',
|
||||
width: self.options.width + 'px',
|
||||
});
|
||||
self.$iframe.css({
|
||||
height: self.options.height + 'px',
|
||||
width: self.options.width + 'px',
|
||||
});
|
||||
}
|
||||
|
||||
/*@
|
||||
postMessage <f> postMessage
|
||||
(event, data) -> <o> post message to epub.js
|
||||
@*/
|
||||
that.postMessage = function(event, data) {
|
||||
self.$iframe.postMessage(event, data);
|
||||
}
|
||||
|
||||
return that;
|
||||
};
|
||||
|
||||
|
|
@ -77,6 +77,16 @@ pandora.ui.document = function() {
|
|||
width: that.width(),
|
||||
zoom: 'fit'
|
||||
})
|
||||
: item.extension == 'epub'
|
||||
? Ox.EpubViewer({
|
||||
height: that.height() - 16,
|
||||
page: pandora.user.ui.documents[item.id]
|
||||
? pandora.user.ui.documents[item.id].position
|
||||
: 1,
|
||||
url: '/documents/' + item.id + '/epub/',
|
||||
width: that.width(),
|
||||
zoom: 'fit'
|
||||
})
|
||||
: item.extension == 'html'
|
||||
? pandora.$ui.textPanel = pandora.ui.textPanel(item, $toolbar)
|
||||
: Ox.ImageViewer({
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@ pandora.imageExtensions = [
|
|||
];
|
||||
|
||||
pandora.documentExtensions = [
|
||||
'pdf', /* 'epub', 'txt', */
|
||||
'pdf', 'epub' /* , 'txt', */
|
||||
].concat(pandora.imageExtensions);
|
||||
|
||||
pandora.uploadDroppedFiles = function(files) {
|
||||
|
|
|
|||
213
static/reader/epub.js
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
"use strict";
|
||||
var reader;
|
||||
var id = document.location.pathname.split('/')[1];
|
||||
var annotations = [];
|
||||
var currentSelection;
|
||||
var fontSize = parseInt(localStorage.epubFontSize || '100', 10)
|
||||
var justSelected = false;
|
||||
|
||||
Ox.load({
|
||||
'UI': {
|
||||
loadCSS: false
|
||||
}
|
||||
}, function() {
|
||||
Ox.$parent.bindMessage(function(data, event) {
|
||||
console.log('got', event, 'data', data)
|
||||
if (event == 'selectAnnotation') {
|
||||
selectAnnotation(data.id)
|
||||
var annotation = annotations.filter(function(a) { return a.id == data.id })[0]
|
||||
if (annotation) {
|
||||
reader.rendition.display(annotation.cfiRange)
|
||||
}
|
||||
} else if (event == 'addAnnotation') {
|
||||
createAnnotation()
|
||||
} else if (event == 'addAnnotations') {
|
||||
if (data.replace) {
|
||||
annotations.forEach(function(a) {
|
||||
reader.rendition.annotations.remove(a.cfiRange)
|
||||
})
|
||||
annotations = []
|
||||
}
|
||||
data.annotations.forEach(function(annotation) {
|
||||
annotations.push(annotation)
|
||||
renderAnnotation(annotation)
|
||||
})
|
||||
} else if (event == 'removeAnnotation') {
|
||||
removeAnnotation(data.id)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
function createAnnotation() {
|
||||
console.log('createAnnotation', currentSelection)
|
||||
if (currentSelection) {
|
||||
/*
|
||||
var range = currentSelection.contents.window.getSelection().getRangeAt(0)
|
||||
console.log(
|
||||
currentSelection.cfiRange,
|
||||
reader.rendition.book.section().cfiFromRange(range).toString()
|
||||
)
|
||||
//currentSelection.cfiRange = reader.rendition.book.section().cfiFromRange(range).toString()
|
||||
*/
|
||||
renderAnnotation(currentSelection)
|
||||
currentSelection.contents.window.getSelection().removeAllRanges();
|
||||
delete currentSelection.contents
|
||||
addAnnotation(currentSelection)
|
||||
document.querySelectorAll('.epubjs-hl.selected').forEach(function(other) {
|
||||
other.classList.remove('selected')
|
||||
})
|
||||
console.log('create annot')
|
||||
currentSelection = null
|
||||
}
|
||||
}
|
||||
|
||||
function addAnnotation(annotation) {
|
||||
annotations.push(annotation)
|
||||
Ox.$parent.postMessage('addAnnotation', annotation)
|
||||
}
|
||||
|
||||
function selectAnnotation(id) {
|
||||
$('.epubjs-hl.selected').each(function(i, g) {
|
||||
g.classList.remove('selected')
|
||||
})
|
||||
$('.epubjs-hl[data-id='+id+']').each(function(i, g) {
|
||||
g.classList.add('selected')
|
||||
})
|
||||
}
|
||||
|
||||
function deselectAnnotation(id) {
|
||||
$('.epubjs-hl[data-id='+id+']').each(function(i, g) {
|
||||
g.classList.remove('selected')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function deselectAllAnnotations() {
|
||||
var ids = []
|
||||
document.querySelectorAll('.epubjs-hl.selected').forEach(function(g) {
|
||||
g.classList.remove('selected')
|
||||
if (!Ox.contains(ids, id)) {
|
||||
ids.push(id)
|
||||
Ox.$parent.postMessage('selectAnnotation', {id: null})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function removeAnnotation(id) {
|
||||
var a = annotations.filter(function(a) { return a.id == id })[0]
|
||||
if (a) {
|
||||
annotations = annotations.filter(function(annotation) {
|
||||
return annotation.id != id
|
||||
})
|
||||
reader.rendition.annotations.remove(a.cfiRange)
|
||||
}
|
||||
Ox.$parent.postMessage('removeAnnotation', {id: id})
|
||||
}
|
||||
|
||||
function renderAnnotation(annotation) {
|
||||
reader.rendition.annotations.highlight(annotation.cfiRange, {id: annotation.id}, onHighlightClicked);
|
||||
}
|
||||
|
||||
function getText(book, cfiRange, cb) {
|
||||
book.getRange(cfiRange).then(function (range) {
|
||||
var text;
|
||||
if (range) {
|
||||
text = range.toString();
|
||||
}
|
||||
cb(text)
|
||||
})
|
||||
}
|
||||
|
||||
function onHighlightClicked(e) {
|
||||
console.log("highlight clicked", e.target.dataset.epubcfi);
|
||||
if(!e.target.classList.contains('selected')) {
|
||||
document.querySelectorAll('.epubjs-hl.selected').forEach(function(other) {
|
||||
other.classList.remove('selected')
|
||||
})
|
||||
e.target.classList.add('selected')
|
||||
Ox.$parent.postMessage('selectAnnotation', {id: e.target.dataset.id})
|
||||
}
|
||||
}
|
||||
|
||||
document.onreadystatechange = function () {
|
||||
if (document.readyState == "complete") {
|
||||
EPUBJS.filePath = "/static/epub.js/js/libs/";
|
||||
EPUBJS.cssPath = "/static/epub.js/css/";
|
||||
EPUBJS.core.addCss('/static/css/epub.css')
|
||||
// fileStorage.filePath = EPUBJS.filePath;
|
||||
reader = ePubReader(document.location.pathname, {
|
||||
restore: true
|
||||
});
|
||||
var rendition = reader.rendition,
|
||||
book = reader.book;
|
||||
|
||||
rendition.themes.fontSize(fontSize + "%");
|
||||
|
||||
reader.rendition.on('keydown', function(event) {
|
||||
if (event.key == 'Delete') {
|
||||
document.querySelectorAll('.epubjs-hl.selected').forEach(function(a) {
|
||||
removeAnnotation(a.dataset.id)
|
||||
})
|
||||
}
|
||||
if (event.key == 'n' || event.keyCode == 13) {
|
||||
var selected = document.querySelector('.epubjs-hl.selected')
|
||||
console.log('!!', currentSelection, selected)
|
||||
|
||||
if (currentSelection) {
|
||||
if (selected) {
|
||||
deselectAllAnnotations()
|
||||
}
|
||||
createAnnotation()
|
||||
} else if (selected) {
|
||||
console.log('editNote?', selected.dataset.id)
|
||||
}
|
||||
}
|
||||
if (event.keyCode == 61 && event.shiftKey) {
|
||||
fontSize += 10
|
||||
rendition.themes.fontSize(fontSize + "%");
|
||||
localStorage.epubFontSize = fontSize
|
||||
event.preventDefault()
|
||||
} else if (event.keyCode == 173 && event.shiftKey) {
|
||||
fontSize -= 10
|
||||
rendition.themes.fontSize(fontSize + "%");
|
||||
localStorage.epubFontSize = fontSize
|
||||
event.preventDefault()
|
||||
} else if (event.keyCode == 48 && event.shiftKey) {
|
||||
fontSize = 100
|
||||
rendition.themes.fontSize(fontSize + "%");
|
||||
localStorage.epubFontSize = fontSize
|
||||
event.preventDefault()
|
||||
}
|
||||
}).on('mouseup', function(event) {
|
||||
if (!justSelected) {
|
||||
var selection = window.getSelection()
|
||||
if (selection.isCollapsed) {
|
||||
currentSelection = null
|
||||
}
|
||||
if (!currentSelection) {
|
||||
Ox.$parent.postMessage('selectText', false)
|
||||
}
|
||||
}
|
||||
deselectAllAnnotations()
|
||||
justSelected = false
|
||||
})
|
||||
rendition.on("mark", function(cfiRange, contents) {
|
||||
console.log('!! mark', cfiRange)
|
||||
})
|
||||
rendition.on("selected", function(cfiRange, contents) {
|
||||
justSelected = true
|
||||
getText(book, cfiRange, function(text) {
|
||||
var position = cfiRange;
|
||||
currentSelection = {
|
||||
id: Ox.SHA1(cfiRange),
|
||||
cfiRange: cfiRange,
|
||||
position: position,
|
||||
text: text,
|
||||
contents: contents
|
||||
}
|
||||
Ox.$parent.postMessage('selectText', text ? true : false)
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||