679 lines
23 KiB
HTML
679 lines
23 KiB
HTML
<!DOCTYPE html>
|
||
<html class="no-js">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||
<title>{{_('epub Reader')}} | {{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" />
|
||
{% if g.google_site_verification|length > 0 %}
|
||
<meta
|
||
name="google-site-verification"
|
||
content="{{g.google_site_verification}}"
|
||
/>
|
||
{% endif %}
|
||
<link
|
||
rel="apple-touch-icon"
|
||
sizes="140x140"
|
||
href="{{ url_for('static', filename='favicon.ico') }}"
|
||
/>
|
||
<link
|
||
rel="shortcut icon"
|
||
href="{{ url_for('static', filename='favicon.ico') }}"
|
||
/>
|
||
|
||
<link
|
||
rel="stylesheet"
|
||
href="{{ url_for('static', filename='css/libs/normalize.css') }}"
|
||
/>
|
||
<link
|
||
rel="stylesheet"
|
||
href="{{ url_for('static', filename='css/main.css') }}"
|
||
/>
|
||
<link
|
||
rel="stylesheet"
|
||
href="{{ url_for('static', filename='css/popup.css') }}"
|
||
/>
|
||
<link
|
||
rel="stylesheet"
|
||
href="{{ url_for('static', filename='css/reader.css') }}"
|
||
/>
|
||
</head>
|
||
|
||
<body>
|
||
<div id="sidebar">
|
||
<div id="panels">
|
||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||
<!--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="back-button"
|
||
data-view="Back to Books"
|
||
href="{{ url_for('web.index') }}"
|
||
>
|
||
Books
|
||
</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>
|
||
<a id="setting" class="icon-cog">Settings</a>
|
||
<a id="fullscreen" class="icon-resize-full">Fullscreen</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="divider"></div>
|
||
<div id="prev" class="arrow">‹</div>
|
||
<div id="viewer"></div>
|
||
<div id="next" class="arrow">›</div>
|
||
<div class="read-footer">
|
||
<div id="pages-count"></div>
|
||
<div id="progress">0%</div>
|
||
</div>
|
||
<div id="loader">
|
||
<img src="{{ url_for('static', filename='img/loader.gif') }}" />
|
||
</div>
|
||
</div>
|
||
<div class="modal md-effect-1" id="settings-modal">
|
||
<div class="md-content">
|
||
<h3>{{_('Settings')}}</h3>
|
||
<div class="form-group themes" id="themes">
|
||
{{_('Choose a theme below:')}}}<br />
|
||
|
||
<!-- Hardcoded a tick in the light theme button because it is the "default" theme. Need to find a way to do this dynamically on startup-->
|
||
<button
|
||
type="button"
|
||
id="lightTheme"
|
||
class="lightTheme"
|
||
onclick="selectTheme(this.id)"
|
||
>
|
||
<span id="lightSelected">✓</span>{{_('Light')}}
|
||
</button>
|
||
<button
|
||
type="button"
|
||
id="darkTheme"
|
||
class="darkTheme"
|
||
onclick="selectTheme(this.id)"
|
||
>
|
||
<span id="darkSelected"> </span>{{_('Dark')}}
|
||
</button>
|
||
<button
|
||
type="button"
|
||
id="sepiaTheme"
|
||
class="sepiaTheme"
|
||
onclick="selectTheme(this.id)"
|
||
>
|
||
<span id="sepiaSelected"> </span>{{_('Sepia')}}
|
||
</button>
|
||
<button
|
||
type="button"
|
||
id="amberTheme"
|
||
class="amberTheme"
|
||
onclick="selectTheme(this.id)"
|
||
>
|
||
<span id="amberSelected"> </span>{{_('Amber')}}
|
||
</button>
|
||
<button
|
||
type="button"
|
||
id="blackTheme"
|
||
class="blackTheme"
|
||
onclick="selectTheme(this.id)"
|
||
>
|
||
<span id="blackSelected"> </span>{{_('Black')}}
|
||
</button>
|
||
<!-- Custom theme: color input + pickr -->
|
||
<div
|
||
id="customThemeWrapper"
|
||
style="
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin: 5px 8px;
|
||
"
|
||
>
|
||
Custom:
|
||
<!-- Picker-only UI: swatch acts as the picker button, and a small span shows selection tick -->
|
||
<div
|
||
id="customThemeSwatch"
|
||
title="Pick color"
|
||
style="
|
||
width: 30px;
|
||
height: 30px;
|
||
border: 1px solid #ccc;
|
||
border-radius: 3px;
|
||
cursor: pointer;
|
||
"
|
||
></div>
|
||
<span id="customSelected"> </span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<p>
|
||
<input
|
||
type="checkbox"
|
||
id="sidebarReflow"
|
||
name="sidebarReflow"
|
||
/>{{_('Reflow text when sidebars are open.')}}
|
||
</p>
|
||
</div>
|
||
<div class="form-group" id="navigationMode">
|
||
<label class="item">{{ _('Navigation') }}:</label>
|
||
<button type="button" id="navSides" onclick="selectNavMode('sides')">
|
||
<span id="navSidesTick"> </span>{{_('Sides')}}
|
||
</button>
|
||
<button
|
||
type="button"
|
||
id="navGestures"
|
||
onclick="selectNavMode('gestures')"
|
||
>
|
||
<span id="navGesturesTick"> </span>{{_('Gestures (mobile)')}}
|
||
</button>
|
||
</div>
|
||
<div class="form-group">
|
||
<p>
|
||
<input
|
||
type="checkbox"
|
||
id="showPagesCount"
|
||
name="showPagesCount"
|
||
/>{{_('Show pages count')}}
|
||
</p>
|
||
</div>
|
||
<div class="form-group fontSizeWrapper">
|
||
<label>{{ _('Font Size') }}</label>
|
||
<div
|
||
class="font-size-controls"
|
||
style="
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
margin-top: 2px;
|
||
"
|
||
>
|
||
<button
|
||
type="button"
|
||
id="fontSizeDecrease"
|
||
style="padding: 8px 15px; font-size: 18px; cursor: pointer"
|
||
>
|
||
−
|
||
</button>
|
||
<span
|
||
id="fontSizeDisplay"
|
||
style="
|
||
min-width: 60px;
|
||
text-align: center;
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
"
|
||
>100%</span
|
||
>
|
||
<button
|
||
type="button"
|
||
id="fontSizeIncrease"
|
||
style="padding: 8px 15px; font-size: 18px; cursor: pointer"
|
||
>
|
||
+
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="font" id="font">
|
||
<label class="item">{{_('Font')}}:</label>
|
||
<button type="button" id="default" onclick="selectFont(this.id)">
|
||
<span>✓</span>{{_('Default')}}
|
||
</button>
|
||
<button type="button" id="Yahei" onclick="selectFont(this.id)">
|
||
<span></span>{{_('Yahei')}}
|
||
</button>
|
||
<button type="button" id="SimSun" onclick="selectFont(this.id)">
|
||
<span></span>{{_('SimSun')}}
|
||
</button>
|
||
<button type="button" id="KaiTi" onclick="selectFont(this.id)">
|
||
<span></span>{{_('KaiTi')}}
|
||
</button>
|
||
<button type="button" id="Arial" onclick="selectFont(this.id)">
|
||
<span></span>{{_('Arial')}}
|
||
</button>
|
||
</div>
|
||
<div class="layou" id="layout">
|
||
<label class="item">{{ _('Spread') }}:</label>
|
||
<button type="button" id="spread" onclick="spread(this.id)">
|
||
<span>✓</span>{{_('Two columns')}}
|
||
</button>
|
||
<button type="button" id="nonespread" onclick="spread(this.id)">
|
||
<span></span>{{_('One column')}}
|
||
</button>
|
||
</div>
|
||
<div class="closer icon-cancel-circled"></div>
|
||
</div>
|
||
</div>
|
||
<div class="overlay"></div>
|
||
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
|
||
<script src="{{ url_for('static', filename='js/compress/jszip_epub.min.js') }}"></script>
|
||
<script src="{{ url_for('static', filename='js/libs/epub.min.js') }}"></script>
|
||
<script type="text/javascript">
|
||
window.calibre = {
|
||
filePath: "{{ url_for('static', filename='js/libs/') }}",
|
||
cssPath: "{{ url_for('static', filename='css/') }}",
|
||
bookmarkUrl:
|
||
"{{ url_for('web.set_bookmark', book_id=bookid, book_format=book_format) }}",
|
||
bookUrl:
|
||
"{{ url_for('web.serve_book', book_id=bookid, book_format=book_format, anyname='file.epub') }}",
|
||
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
|
||
useBookmarks: "{{ current_user.is_authenticated | tojson }}",
|
||
};
|
||
|
||
// load custom theme color from localStorage (if any)
|
||
var _savedCustomThemeColor = null;
|
||
try {
|
||
_savedCustomThemeColor = localStorage.getItem(
|
||
"calibre.reader.customTheme"
|
||
);
|
||
} catch (e) {}
|
||
|
||
window.themes = {
|
||
darkTheme: {
|
||
bgColor: "#202124",
|
||
css_path: "{{ url_for('static', filename='css/epub_themes.css') }}",
|
||
"title-color": "#fff",
|
||
},
|
||
lightTheme: {
|
||
bgColor: "white",
|
||
css_path: "{{ url_for('static', filename='css/epub_themes.css') }}",
|
||
"title-color": "#4f4f4f",
|
||
},
|
||
sepiaTheme: {
|
||
bgColor: "#ece1ca",
|
||
css_path: "{{ url_for('static', filename='css/epub_themes.css') }}",
|
||
"title-color": "#4f4f4f",
|
||
},
|
||
amberTheme: {
|
||
bgColor: "#e8bf5e",
|
||
css_path: "{{ url_for('static', filename='css/epub_themes.css') }}",
|
||
"title-color": "#2f2f2f",
|
||
},
|
||
blackTheme: {
|
||
bgColor: "black",
|
||
css_path: "{{ url_for('static', filename='css/epub_themes.css') }}",
|
||
"title-color": "#fff",
|
||
},
|
||
customTheme: {
|
||
bgColor: _savedCustomThemeColor || "#ffffff",
|
||
css_path: "{{ url_for('static', filename='css/epub_themes.css') }}",
|
||
"title-color": "#4f4f4f",
|
||
},
|
||
};
|
||
|
||
function selectTheme(id) {
|
||
// Collect all span tick markers inside themes and clear them safely
|
||
var tickSpans = document
|
||
.getElementById("themes")
|
||
.querySelectorAll("span");
|
||
tickSpans.forEach(function (tickSpan) {
|
||
try {
|
||
tickSpan.textContent = "";
|
||
} catch (e) {}
|
||
});
|
||
|
||
// If the theme button exists, set its inner span to a tick. Otherwise set any span with the matching id.
|
||
var el = document.getElementById(id);
|
||
if (el) {
|
||
var sp = el.querySelector("span");
|
||
if (sp) sp.textContent = "✓";
|
||
} else {
|
||
var spById =
|
||
document.getElementById(id + "Selected") ||
|
||
document.getElementById("customSelected");
|
||
if (spById) spById.textContent = "✓";
|
||
}
|
||
|
||
// Saving theme to local storage
|
||
localStorage.setItem("calibre.reader.theme", id);
|
||
|
||
// If selecting custom theme, ensure epubjs theme is registered with chosen bg color
|
||
if (id === "customTheme") {
|
||
var customColor = window.themes.customTheme.bgColor || "#ffffff";
|
||
try {
|
||
if (reader && reader.rendition && reader.rendition.themes) {
|
||
reader.rendition.themes.register("customTheme", {
|
||
body: {
|
||
background: customColor,
|
||
},
|
||
});
|
||
reader.rendition.themes.select("customTheme");
|
||
}
|
||
} catch (e) {
|
||
console.error("Failed to register/select customTheme", e);
|
||
}
|
||
} else {
|
||
// Apply theme to epubjs iframe
|
||
try {
|
||
reader.rendition.themes.select(id);
|
||
} catch (e) {}
|
||
}
|
||
|
||
// Apply theme to rest of the page.
|
||
document.getElementById("main").style.backgroundColor =
|
||
themes[id]["bgColor"];
|
||
document.getElementById("titlebar").style.color =
|
||
themes[id]["title-color"] || "#fff";
|
||
document.getElementById("progress").style.color =
|
||
themes[id]["title-color"] || "#fff";
|
||
}
|
||
|
||
// font size settings logic
|
||
let currentFontSize = 100; // default 100%
|
||
const minFontSize = 50;
|
||
const maxFontSize = 300;
|
||
const stepSize = 5;
|
||
|
||
const fontSizeDisplay = document.getElementById("fontSizeDisplay");
|
||
const fontSizeDecrease = document.getElementById("fontSizeDecrease");
|
||
const fontSizeIncrease = document.getElementById("fontSizeIncrease");
|
||
|
||
function updateFontSize(newSize) {
|
||
if (newSize < minFontSize) newSize = minFontSize;
|
||
if (newSize > maxFontSize) newSize = maxFontSize;
|
||
|
||
currentFontSize = newSize;
|
||
fontSizeDisplay.textContent = newSize + "%";
|
||
localStorage.setItem("calibre.reader.fontSize", newSize);
|
||
if (reader && reader.rendition) {
|
||
reader.rendition.themes.fontSize(`${newSize}%`);
|
||
}
|
||
}
|
||
|
||
// Restore saved font size on load
|
||
const savedFontSize = localStorage.getItem("calibre.reader.fontSize");
|
||
if (savedFontSize) {
|
||
currentFontSize = parseInt(savedFontSize);
|
||
fontSizeDisplay.textContent = savedFontSize + "%";
|
||
}
|
||
|
||
fontSizeDecrease.addEventListener("click", function () {
|
||
updateFontSize(currentFontSize - stepSize);
|
||
});
|
||
|
||
fontSizeIncrease.addEventListener("click", function () {
|
||
updateFontSize(currentFontSize + stepSize);
|
||
});
|
||
|
||
let defaultFont;
|
||
|
||
window.selectFont = function (id) {
|
||
if (!defaultFont) {
|
||
defaultFont = reader.rendition.getContents()[0]?.css("font-family");
|
||
}
|
||
|
||
spans = document.getElementById("font").querySelectorAll("span");
|
||
for (var i = 0; i < spans.length; i++) {
|
||
spans[i].textContent = "";
|
||
}
|
||
document.getElementById(id).querySelector("span").textContent = "✓";
|
||
|
||
// Save font selection to localStorage
|
||
localStorage.setItem("calibre.reader.font", id);
|
||
|
||
if (id == "default") {
|
||
reader.rendition.themes.font(defaultFont);
|
||
return;
|
||
}
|
||
reader.rendition.themes.font(id);
|
||
};
|
||
|
||
function spread(id) {
|
||
spans = document.getElementById("layout").querySelectorAll("span");
|
||
for (var i = 0; i < spans.length; i++) {
|
||
spans[i].textContent = "";
|
||
}
|
||
document.getElementById(id).querySelector("span").textContent = "✓";
|
||
reader.rendition.spread(id === "spread" ? true : "none");
|
||
}
|
||
function selectNavMode(mode) {
|
||
var tickSides = document.getElementById("navSidesTick");
|
||
var tickGest = document.getElementById("navGesturesTick");
|
||
tickSides.textContent = mode === "sides" ? "✓" : "";
|
||
tickGest.textContent = mode === "gestures" ? "✓" : "";
|
||
localStorage.setItem("calibre.reader.navMode", mode);
|
||
if (window.applyNavigationMode) {
|
||
window.applyNavigationMode(mode);
|
||
}
|
||
}
|
||
// Initialize nav mode on load
|
||
(function () {
|
||
var savedMode =
|
||
localStorage.getItem("calibre.reader.navMode") || "sizes";
|
||
selectNavMode(savedMode);
|
||
})();
|
||
|
||
// Pages counter visibility setting
|
||
(function () {
|
||
var checkbox = document.getElementById("showPagesCount");
|
||
var pagesEl = document.getElementById("pages-count");
|
||
var key = "calibre.reader.showPages";
|
||
var saved = localStorage.getItem(key);
|
||
var show = saved === null ? true : saved === "true";
|
||
if (checkbox) checkbox.checked = show;
|
||
if (pagesEl) pagesEl.style.display = show ? "" : "none";
|
||
if (checkbox) {
|
||
checkbox.addEventListener("change", function () {
|
||
var val = checkbox.checked;
|
||
localStorage.setItem(key, String(val));
|
||
var target = document.getElementById("pages-count");
|
||
if (target) target.style.visibility = val ? "visible" : "hidden";
|
||
});
|
||
}
|
||
})();
|
||
</script>
|
||
<!-- Pickr color picker (local loader / fallback) -->
|
||
<link
|
||
rel="stylesheet"
|
||
href="{{ url_for('static', filename='js/libs/pickr/pickr.min.css') }}"
|
||
/>
|
||
<script src="{{ url_for('static', filename='js/libs/pickr/pickr.min.js') }}"></script>
|
||
|
||
<script type="text/javascript">
|
||
// Initialize custom theme UI and Pickr once Pickr is available
|
||
(function () {
|
||
var swatch = document.getElementById("customThemeSwatch");
|
||
var saved =
|
||
window.themes &&
|
||
window.themes.customTheme &&
|
||
window.themes.customTheme.bgColor
|
||
? window.themes.customTheme.bgColor
|
||
: "#ffffff";
|
||
if (swatch) swatch.style.background = saved;
|
||
|
||
function _hexToRgb(hex) {
|
||
hex = hex.replace("#", "");
|
||
if (hex.length === 3) {
|
||
hex = hex
|
||
.split("")
|
||
.map(function (h) {
|
||
return h + h;
|
||
})
|
||
.join("");
|
||
}
|
||
var bigint = parseInt(hex, 16);
|
||
return {
|
||
r: (bigint >> 16) & 255,
|
||
g: (bigint >> 8) & 255,
|
||
b: bigint & 255,
|
||
};
|
||
}
|
||
|
||
// Better contrast decision using WCAG relative luminance and contrast ratio
|
||
// Returns true if black text is the better choice (i.e. background is light)
|
||
function _isLight(hex) {
|
||
try {
|
||
var rgb = _hexToRgb(hex);
|
||
// convert 0-255 to 0-1
|
||
var srgb = { r: rgb.r / 255, g: rgb.g / 255, b: rgb.b / 255 };
|
||
function lin(c) {
|
||
return c <= 0.03928
|
||
? c / 12.92
|
||
: Math.pow((c + 0.055) / 1.055, 2.4);
|
||
}
|
||
var R = lin(srgb.r),
|
||
G = lin(srgb.g),
|
||
B = lin(srgb.b);
|
||
var L = 0.2126 * R + 0.7152 * G + 0.0722 * B; // relative luminance
|
||
|
||
// contrast ratios against black (L=0) and white (L=1)
|
||
var contrastWithBlack = (L + 0.05) / (0.0 + 0.05);
|
||
var contrastWithWhite = (1.0 + 0.05) / (L + 0.05);
|
||
|
||
// pick the text color that gives higher contrast. If black gives >= contrast, treat bg as light
|
||
return contrastWithBlack >= contrastWithWhite;
|
||
} catch (e) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
function applyCustomColor(hex) {
|
||
if (!hex) return;
|
||
if (hex[0] !== "#") hex = "#" + hex;
|
||
window.themes.customTheme.bgColor = hex;
|
||
try {
|
||
localStorage.setItem("calibre.reader.customTheme", hex);
|
||
} catch (e) {}
|
||
if (swatch) swatch.style.background = hex;
|
||
|
||
// Mark theme as selected
|
||
try {
|
||
localStorage.setItem("calibre.reader.theme", "customTheme");
|
||
} catch (e) {}
|
||
// Clear ticks and set custom tick
|
||
var tickSpans = document
|
||
.getElementById("themes")
|
||
.querySelectorAll("span");
|
||
tickSpans.forEach(function (ts) {
|
||
ts.textContent = "";
|
||
});
|
||
var customTick = document.getElementById("customSelected");
|
||
if (customTick) customTick.textContent = "✓";
|
||
|
||
// Compute title/text color based on contrast (black or white)
|
||
var titleColor = _isLight(hex) ? "#000000" : "#ffffff";
|
||
try {
|
||
document.getElementById("main").style.backgroundColor = hex;
|
||
document.getElementById("titlebar").style.color = titleColor;
|
||
document.getElementById("progress").style.color = titleColor;
|
||
} catch (e) {}
|
||
|
||
// Persist title-color in themes map so selectTheme reads a consistent value
|
||
try {
|
||
window.themes.customTheme["title-color"] = titleColor;
|
||
} catch (e) {}
|
||
|
||
// Register and select theme in epub rendition (include text color)
|
||
try {
|
||
if (reader && reader.rendition && reader.rendition.themes) {
|
||
reader.rendition.themes.register("customTheme", {
|
||
body: { background: hex, color: titleColor },
|
||
});
|
||
reader.rendition.themes.select("customTheme");
|
||
}
|
||
} catch (e) {
|
||
console.error("Failed to apply custom theme to reader", e);
|
||
}
|
||
}
|
||
|
||
// Delay init until Pickr is available
|
||
function ensurePickrAndInit() {
|
||
if (window.Pickr) {
|
||
try {
|
||
var pickr = Pickr.create({
|
||
el: "#customThemeSwatch",
|
||
theme: "classic",
|
||
default: saved,
|
||
components: {
|
||
preview: true,
|
||
opacity: false,
|
||
hue: true,
|
||
interaction: {
|
||
hex: true,
|
||
input: true,
|
||
save: true,
|
||
},
|
||
},
|
||
});
|
||
|
||
// Immediate apply when color changes in the picker
|
||
pickr.on("change", function (color, instance) {
|
||
try {
|
||
var hex = color.toHEXA().toString();
|
||
if (swatch) swatch.style.background = hex;
|
||
applyCustomColor(hex);
|
||
} catch (e) {}
|
||
});
|
||
|
||
// Also respond to save (some pickr configs use save)
|
||
pickr.on("save", function (color, instance) {
|
||
try {
|
||
var hex = color.toHEXA().toString();
|
||
if (swatch) swatch.style.background = hex;
|
||
applyCustomColor(hex);
|
||
pickr.hide();
|
||
} catch (e) {}
|
||
});
|
||
|
||
// Clicking swatch opens pickr
|
||
if (swatch)
|
||
swatch.addEventListener("click", function () {
|
||
pickr.show();
|
||
});
|
||
} catch (e) {
|
||
console.error("Pickr init failed", e);
|
||
}
|
||
return;
|
||
}
|
||
// wait a bit
|
||
setTimeout(ensurePickrAndInit, 150);
|
||
}
|
||
|
||
ensurePickrAndInit();
|
||
})();
|
||
</script>
|
||
|
||
<script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script>
|
||
<script src="{{ url_for('static', filename='js/libs/hammer.min.js') }}"></script>
|
||
<script src="{{ url_for('static', filename='js/libs/reader.min.js') }}"></script>
|
||
<script src="{{ url_for('static', filename='js/reading/epub.js') }}"></script>
|
||
</body>
|
||
</html>
|