Merge
This commit is contained in:
commit
ac8763f4f5
|
@ -65,6 +65,7 @@ class JSONRenderer
|
||||||
public static function getFullBookContentArray ($book) {
|
public static function getFullBookContentArray ($book) {
|
||||||
global $config;
|
global $config;
|
||||||
$out = self::getBookContentArray ($book);
|
$out = self::getBookContentArray ($book);
|
||||||
|
$database = GetUrlParam (DB);
|
||||||
|
|
||||||
$out ["coverurl"] = Data::getLink ($book, "jpg", "image/jpeg", Link::OPDS_IMAGE_TYPE, "cover.jpg", NULL)->hrefXhtml ();
|
$out ["coverurl"] = Data::getLink ($book, "jpg", "image/jpeg", Link::OPDS_IMAGE_TYPE, "cover.jpg", NULL)->hrefXhtml ();
|
||||||
$out ["thumbnailurl"] = Data::getLink ($book, "jpg", "image/jpeg", Link::OPDS_THUMBNAIL_TYPE, "cover.jpg", NULL, NULL, $config['cops_html_thumbnail_height'] * 2)->hrefXhtml ();
|
$out ["thumbnailurl"] = Data::getLink ($book, "jpg", "image/jpeg", Link::OPDS_THUMBNAIL_TYPE, "cover.jpg", NULL, NULL, $config['cops_html_thumbnail_height'] * 2)->hrefXhtml ();
|
||||||
|
@ -72,10 +73,13 @@ class JSONRenderer
|
||||||
$out ["datas"] = array ();
|
$out ["datas"] = array ();
|
||||||
$dataKindle = $book->GetMostInterestingDataToSendToKindle ();
|
$dataKindle = $book->GetMostInterestingDataToSendToKindle ();
|
||||||
foreach ($book->getDatas() as $data) {
|
foreach ($book->getDatas() as $data) {
|
||||||
$tab = array ("id" => $data->id, "format" => $data->format, "url" => $data->getHtmlLink (), "mail" => 0);
|
$tab = array ("id" => $data->id, "format" => $data->format, "url" => $data->getHtmlLink (), "mail" => 0, "readerUrl" => "");
|
||||||
if (!empty ($config['cops_mail_configuration']) && !is_null ($dataKindle) && $data->id == $dataKindle->id) {
|
if (!empty ($config['cops_mail_configuration']) && !is_null ($dataKindle) && $data->id == $dataKindle->id) {
|
||||||
$tab ["mail"] = 1;
|
$tab ["mail"] = 1;
|
||||||
}
|
}
|
||||||
|
if ($data->format == "EPUB") {
|
||||||
|
$tab ["readerUrl"] = "epubreader.php?data={$data->id}&db={$database}";
|
||||||
|
}
|
||||||
array_push ($out ["datas"], $tab);
|
array_push ($out ["datas"], $tab);
|
||||||
}
|
}
|
||||||
$out ["authors"] = array ();
|
$out ["authors"] = array ();
|
||||||
|
|
74
epubfs.php
Normal file
74
epubfs.php
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once ("config.php");
|
||||||
|
require_once ("base.php");
|
||||||
|
require_once ("book.php");
|
||||||
|
require_once ("resources/php-epub-meta/epub.php");
|
||||||
|
|
||||||
|
function notFound () {
|
||||||
|
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
|
||||||
|
header("Status: 404 Not Found");
|
||||||
|
|
||||||
|
$_SERVER['REDIRECT_STATUS'] = 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
$idData = getURLParam ("data", NULL);
|
||||||
|
$add = "data=$idData&";
|
||||||
|
if (!is_null (GetUrlParam (DB))) $add .= DB . "=" . GetUrlParam (DB) . "&";
|
||||||
|
$myBook = Book::getBookByDataId($idData);
|
||||||
|
|
||||||
|
$book = new EPub ($myBook->getFilePath ("EPUB", $idData));
|
||||||
|
|
||||||
|
$book->initSpineComponent ();
|
||||||
|
|
||||||
|
if (!isset ($_GET["comp"])) {
|
||||||
|
notFound ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$component = $_GET["comp"];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$data = $book->component ($component);
|
||||||
|
$directory = dirname ($component);
|
||||||
|
|
||||||
|
$callback = function ($m) use ($book, $component, $add) {
|
||||||
|
$method = $m[1];
|
||||||
|
$path = $m[2];
|
||||||
|
$end = "";
|
||||||
|
if (preg_match ("/^src:/", $method)) {
|
||||||
|
$end = ")";
|
||||||
|
}
|
||||||
|
if (preg_match ("/^#/", $path)) {
|
||||||
|
return "{$method}'{$path}'{$end}";
|
||||||
|
}
|
||||||
|
$hash = "";
|
||||||
|
if (preg_match ("/^(.+)#(.+)$/", $path, $matches)) {
|
||||||
|
$path = $matches [1];
|
||||||
|
$hash = "#" . $matches [2];
|
||||||
|
}
|
||||||
|
$comp = $book->getComponentName ($component, $path);
|
||||||
|
if (!$comp) return "{$method}'#'{$end}";
|
||||||
|
$out = "{$method}'epubfs.php?{$add}comp={$comp}{$hash}'{$end}";
|
||||||
|
if ($end) {
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
return str_replace ("&", "&", $out);
|
||||||
|
};
|
||||||
|
|
||||||
|
$data = preg_replace_callback ("/(src=)[\"']([^:]*?)[\"']/", $callback, $data);
|
||||||
|
$data = preg_replace_callback ("/(href=)[\"']([^:]*?)[\"']/", $callback, $data);
|
||||||
|
$data = preg_replace_callback ("/(\@import\s+)[\"'](.*?)[\"'];/", $callback, $data);
|
||||||
|
$data = preg_replace_callback ("/(src:\s*url\()(.*?)\)/", $callback, $data);
|
||||||
|
|
||||||
|
$expires = 60*60*24*14;
|
||||||
|
header("Pragma: public");
|
||||||
|
header("Cache-Control: maxage=".$expires);
|
||||||
|
header('Expires: ' . gmdate('D, d M Y H:i:s', time()+$expires) . ' GMT');
|
||||||
|
header ("Content-Type: " . $book->componentContentType($component));
|
||||||
|
echo $data;
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
error_log ($e);
|
||||||
|
notFound ();
|
||||||
|
}
|
68
epubreader.php
Normal file
68
epubreader.php
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once ("config.php");
|
||||||
|
require_once ("base.php");
|
||||||
|
require_once ("book.php");
|
||||||
|
require_once ("resources/php-epub-meta/epub.php");
|
||||||
|
|
||||||
|
header ("Content-Type: text/html;charset=utf-8");
|
||||||
|
|
||||||
|
$idData = getURLParam ("data", NULL);
|
||||||
|
$add = "data=$idData&";
|
||||||
|
if (!is_null (GetUrlParam (DB))) $add .= DB . "=" . GetUrlParam (DB) . "&";
|
||||||
|
$myBook = Book::getBookByDataId($idData);
|
||||||
|
|
||||||
|
$book = new EPub ($myBook->getFilePath ("EPUB", $idData));
|
||||||
|
$book->initSpineComponent ();
|
||||||
|
|
||||||
|
?>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<meta http-equiv="imagetoolbar" content="no" />
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height, user-scalable=no" />
|
||||||
|
<title>COPS's Epub Reader</title>
|
||||||
|
<script type="text/javascript" src="<?php echo getUrlWithVersion("resources/monocle/scripts/monocore.js") ?>"></script>
|
||||||
|
<script type="text/javascript" src="<?php echo getUrlWithVersion("resources/monocle/scripts/monoctrl.js") ?>"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="<?php echo getUrlWithVersion("resources/monocle/styles/monocore.css") ?>" media="screen" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="<?php echo getUrlWithVersion("resources/monocle/styles/monoctrl.css") ?>" media="screen" />
|
||||||
|
<script type="text/javascript">
|
||||||
|
Monocle.DEBUG = true;
|
||||||
|
var bookData = {
|
||||||
|
getComponents: function () {
|
||||||
|
<?php echo "return [" . implode (", ", array_map (function ($comp) { return "'" . $comp . "'"; }, $book->components ())) . "];"; ?>
|
||||||
|
},
|
||||||
|
getContents: function () {
|
||||||
|
<?php echo "return [" . implode (", ", array_map (function ($content) { return "{title: '" . $content["title"] . "', src: '". $content["src"] . "'}"; }, $book->contents ())) . "];"; ?>
|
||||||
|
},
|
||||||
|
getComponent: function (componentId) {
|
||||||
|
return { url: "epubfs.php?<?php echo $add ?>comp=" + componentId };
|
||||||
|
},
|
||||||
|
getMetaData: function(key) {
|
||||||
|
return {
|
||||||
|
title: "<?php echo $myBook->title ?>",
|
||||||
|
creator: "Inventive Labs"
|
||||||
|
}[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="<?php echo getUrlWithVersion("styles/cops-monocle.js") ?>"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="<?php echo getUrlWithVersion("styles/cops-monocle.css") ?>" media="screen" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="readerBg">
|
||||||
|
<div class="board"></div>
|
||||||
|
<div class="jacket"></div>
|
||||||
|
<div class="dummyPage"></div>
|
||||||
|
<div class="dummyPage"></div>
|
||||||
|
<div class="dummyPage"></div>
|
||||||
|
<div class="dummyPage"></div>
|
||||||
|
<div class="dummyPage"></div>
|
||||||
|
</div>
|
||||||
|
<div id="readerCntr">
|
||||||
|
<div id="reader"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
5641
resources/monocle/scripts/monocore.js
Normal file
5641
resources/monocle/scripts/monocore.js
Normal file
File diff suppressed because it is too large
Load diff
985
resources/monocle/scripts/monoctrl.js
Normal file
985
resources/monocle/scripts/monoctrl.js
Normal file
|
@ -0,0 +1,985 @@
|
||||||
|
Monocle.Controls.Contents = function (reader) {
|
||||||
|
|
||||||
|
var API = { constructor: Monocle.Controls.Contents }
|
||||||
|
var k = API.constants = API.constructor;
|
||||||
|
var p = API.properties = {
|
||||||
|
reader: reader
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createControlElements() {
|
||||||
|
var div = reader.dom.make('div', 'controls_contents_container');
|
||||||
|
contentsForBook(div, reader.getBook());
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function contentsForBook(div, book) {
|
||||||
|
while (div.hasChildNodes()) {
|
||||||
|
div.removeChild(div.firstChild);
|
||||||
|
}
|
||||||
|
var list = div.dom.append('ol', 'controls_contents_list');
|
||||||
|
|
||||||
|
var contents = book.properties.contents;
|
||||||
|
for (var i = 0; i < contents.length; ++i) {
|
||||||
|
chapterBuilder(list, contents[i], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function chapterBuilder(list, chp, padLvl) {
|
||||||
|
var index = list.childNodes.length;
|
||||||
|
var li = list.dom.append('li', 'controls_contents_chapter', index);
|
||||||
|
var span = li.dom.append(
|
||||||
|
'span',
|
||||||
|
'controls_contents_chapterTitle',
|
||||||
|
index,
|
||||||
|
{ html: chp.title }
|
||||||
|
);
|
||||||
|
span.style.paddingLeft = padLvl + "em";
|
||||||
|
|
||||||
|
var invoked = function () {
|
||||||
|
p.reader.skipToChapter(chp.src);
|
||||||
|
p.reader.hideControl(API);
|
||||||
|
}
|
||||||
|
|
||||||
|
Monocle.Events.listenForTap(li, invoked, 'controls_contents_chapter_active');
|
||||||
|
|
||||||
|
if (chp.children) {
|
||||||
|
for (var i = 0; i < chp.children.length; ++i) {
|
||||||
|
chapterBuilder(list, chp.children[i], padLvl + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
API.createControlElements = createControlElements;
|
||||||
|
|
||||||
|
return API;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
Monocle.Controls.Magnifier = function (reader) {
|
||||||
|
|
||||||
|
var API = { constructor: Monocle.Controls.Magnifier }
|
||||||
|
var k = API.constants = API.constructor;
|
||||||
|
var p = API.properties = {
|
||||||
|
buttons: [],
|
||||||
|
magnified: false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
p.reader = reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createControlElements(holder) {
|
||||||
|
var btn = holder.dom.make('div', 'controls_magnifier_button');
|
||||||
|
btn.smallA = btn.dom.append('span', 'controls_magnifier_a', { text: 'A' });
|
||||||
|
btn.largeA = btn.dom.append('span', 'controls_magnifier_A', { text: 'A' });
|
||||||
|
p.buttons.push(btn);
|
||||||
|
Monocle.Events.listenForTap(btn, toggleMagnification);
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function toggleMagnification(evt) {
|
||||||
|
var opacities;
|
||||||
|
p.magnified = !p.magnified;
|
||||||
|
if (p.magnified) {
|
||||||
|
opacities = [0.3, 1];
|
||||||
|
p.reader.formatting.setFontScale(k.MAGNIFICATION, true);
|
||||||
|
} else {
|
||||||
|
opacities = [1, 0.3];
|
||||||
|
p.reader.formatting.setFontScale(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < p.buttons.length; i++) {
|
||||||
|
p.buttons[i].smallA.style.opacity = opacities[0];
|
||||||
|
p.buttons[i].largeA.style.opacity = opacities[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
API.createControlElements = createControlElements;
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
return API;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Monocle.Controls.Magnifier.MAGNIFICATION = 1.2;
|
||||||
|
// A panel is an invisible column of interactivity. When contact occurs
|
||||||
|
// (mousedown, touchstart), the panel expands to the full width of its
|
||||||
|
// container, to catch all interaction events and prevent them from hitting
|
||||||
|
// other things.
|
||||||
|
//
|
||||||
|
// Panels are used primarily to provide hit zones for page flipping
|
||||||
|
// interactions, but you can do whatever you like with them.
|
||||||
|
//
|
||||||
|
// After instantiating a panel and adding it to the reader as a control,
|
||||||
|
// you can call listenTo() with a hash of methods for any of 'start', 'move'
|
||||||
|
// 'end' and 'cancel'.
|
||||||
|
//
|
||||||
|
Monocle.Controls.Panel = function () {
|
||||||
|
|
||||||
|
var API = { constructor: Monocle.Controls.Panel }
|
||||||
|
var k = API.constants = API.constructor;
|
||||||
|
var p = API.properties = {
|
||||||
|
evtCallbacks: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createControlElements(cntr) {
|
||||||
|
p.div = cntr.dom.make('div', k.CLS.panel);
|
||||||
|
p.div.dom.setStyles(k.DEFAULT_STYLES);
|
||||||
|
Monocle.Events.listenForContact(
|
||||||
|
p.div,
|
||||||
|
{
|
||||||
|
'start': start,
|
||||||
|
'move': move,
|
||||||
|
'end': end,
|
||||||
|
'cancel': cancel
|
||||||
|
},
|
||||||
|
{ useCapture: false }
|
||||||
|
);
|
||||||
|
return p.div;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setDirection(dir) {
|
||||||
|
p.direction = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function listenTo(evtCallbacks) {
|
||||||
|
p.evtCallbacks = evtCallbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function deafen() {
|
||||||
|
p.evtCallbacks = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function start(evt) {
|
||||||
|
p.contact = true;
|
||||||
|
evt.m.offsetX += p.div.offsetLeft;
|
||||||
|
evt.m.offsetY += p.div.offsetTop;
|
||||||
|
expand();
|
||||||
|
invoke('start', evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function move(evt) {
|
||||||
|
if (!p.contact) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
invoke('move', evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function end(evt) {
|
||||||
|
if (!p.contact) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Monocle.Events.deafenForContact(p.div, p.listeners);
|
||||||
|
contract();
|
||||||
|
p.contact = false;
|
||||||
|
invoke('end', evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function cancel(evt) {
|
||||||
|
if (!p.contact) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Monocle.Events.deafenForContact(p.div, p.listeners);
|
||||||
|
contract();
|
||||||
|
p.contact = false;
|
||||||
|
invoke('cancel', evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function invoke(evtType, evt) {
|
||||||
|
if (p.evtCallbacks[evtType]) {
|
||||||
|
p.evtCallbacks[evtType](p.direction, evt.m.offsetX, evt.m.offsetY, API);
|
||||||
|
}
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function expand() {
|
||||||
|
if (p.expanded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p.div.dom.addClass(k.CLS.expanded);
|
||||||
|
p.expanded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function contract(evt) {
|
||||||
|
if (!p.expanded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p.div.dom.removeClass(k.CLS.expanded);
|
||||||
|
p.expanded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
API.createControlElements = createControlElements;
|
||||||
|
API.listenTo = listenTo;
|
||||||
|
API.deafen = deafen;
|
||||||
|
API.expand = expand;
|
||||||
|
API.contract = contract;
|
||||||
|
API.setDirection = setDirection;
|
||||||
|
|
||||||
|
return API;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Monocle.Controls.Panel.CLS = {
|
||||||
|
panel: 'panel',
|
||||||
|
expanded: 'controls_panel_expanded'
|
||||||
|
}
|
||||||
|
Monocle.Controls.Panel.DEFAULT_STYLES = {
|
||||||
|
position: 'absolute',
|
||||||
|
height: '100%'
|
||||||
|
}
|
||||||
|
;
|
||||||
|
Monocle.Controls.PlaceSaver = function (bookId) {
|
||||||
|
|
||||||
|
var API = { constructor: Monocle.Controls.PlaceSaver }
|
||||||
|
var k = API.constants = API.constructor;
|
||||||
|
var p = API.properties = {}
|
||||||
|
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
applyToBook(bookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function assignToReader(reader) {
|
||||||
|
p.reader = reader;
|
||||||
|
p.reader.listen('monocle:turn', savePlaceToCookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function applyToBook(bookId) {
|
||||||
|
p.bkTitle = bookId.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||||
|
p.prefix = k.COOKIE_NAMESPACE + p.bkTitle + ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setCookie(key, value, days) {
|
||||||
|
var expires = "";
|
||||||
|
if (days) {
|
||||||
|
var d = new Date();
|
||||||
|
d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
|
expires = "; expires="+d.toGMTString();
|
||||||
|
}
|
||||||
|
var path = "; path=/";
|
||||||
|
document.cookie = p.prefix + key + "=" + value + expires + path;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getCookie(key) {
|
||||||
|
if (!document.cookie) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var regex = new RegExp(p.prefix + key + "=(.+?)(;|$)");
|
||||||
|
var matches = document.cookie.match(regex);
|
||||||
|
if (matches) {
|
||||||
|
return matches[1];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function savePlaceToCookie() {
|
||||||
|
var place = p.reader.getPlace();
|
||||||
|
setCookie(
|
||||||
|
"component",
|
||||||
|
encodeURIComponent(place.componentId()),
|
||||||
|
k.COOKIE_EXPIRES_IN_DAYS
|
||||||
|
);
|
||||||
|
setCookie(
|
||||||
|
"percent",
|
||||||
|
place.percentageThrough(),
|
||||||
|
k.COOKIE_EXPIRES_IN_DAYS
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function savedPlace() {
|
||||||
|
var locus = {
|
||||||
|
componentId: getCookie('component'),
|
||||||
|
percent: getCookie('percent')
|
||||||
|
}
|
||||||
|
if (locus.componentId && locus.percent) {
|
||||||
|
locus.componentId = decodeURIComponent(locus.componentId);
|
||||||
|
locus.percent = parseFloat(locus.percent);
|
||||||
|
return locus;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function restorePlace() {
|
||||||
|
var locus = savedPlace();
|
||||||
|
if (locus) {
|
||||||
|
p.reader.moveTo(locus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
API.assignToReader = assignToReader;
|
||||||
|
API.savedPlace = savedPlace;
|
||||||
|
API.restorePlace = restorePlace;
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
return API;
|
||||||
|
}
|
||||||
|
|
||||||
|
Monocle.Controls.PlaceSaver.COOKIE_NAMESPACE = "monocle.controls.placesaver.";
|
||||||
|
Monocle.Controls.PlaceSaver.COOKIE_EXPIRES_IN_DAYS = 7; // Set to 0 for session-based expiry.
|
||||||
|
;
|
||||||
|
Monocle.Controls.Scrubber = function (reader) {
|
||||||
|
|
||||||
|
var API = { constructor: Monocle.Controls.Scrubber }
|
||||||
|
var k = API.constants = API.constructor;
|
||||||
|
var p = API.properties = {}
|
||||||
|
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
p.reader = reader;
|
||||||
|
p.reader.listen('monocle:turn', updateNeedles);
|
||||||
|
updateNeedles();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function pixelToPlace(x, cntr) {
|
||||||
|
if (!p.componentIds) {
|
||||||
|
p.componentIds = p.reader.getBook().properties.componentIds;
|
||||||
|
p.componentWidth = 100 / p.componentIds.length;
|
||||||
|
}
|
||||||
|
var pc = (x / cntr.offsetWidth) * 100;
|
||||||
|
var cmpt = p.componentIds[Math.floor(pc / p.componentWidth)];
|
||||||
|
var cmptPc = ((pc % p.componentWidth) / p.componentWidth);
|
||||||
|
return { componentId: cmpt, percentageThrough: cmptPc };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function placeToPixel(place, cntr) {
|
||||||
|
if (!p.componentIds) {
|
||||||
|
p.componentIds = p.reader.getBook().properties.componentIds;
|
||||||
|
p.componentWidth = 100 / p.componentIds.length;
|
||||||
|
}
|
||||||
|
var componentIndex = p.componentIds.indexOf(place.componentId());
|
||||||
|
var pc = p.componentWidth * componentIndex;
|
||||||
|
pc += place.percentageThrough() * p.componentWidth;
|
||||||
|
return Math.round((pc / 100) * cntr.offsetWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateNeedles() {
|
||||||
|
if (p.hidden || !p.reader.dom.find(k.CLS.container)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var place = p.reader.getPlace();
|
||||||
|
var x = placeToPixel(place, p.reader.dom.find(k.CLS.container));
|
||||||
|
var needle, i = 0;
|
||||||
|
for (var i = 0, needle; needle = p.reader.dom.find(k.CLS.needle, i); ++i) {
|
||||||
|
setX(needle, x - needle.offsetWidth / 2);
|
||||||
|
p.reader.dom.find(k.CLS.trail, i).style.width = x + "px";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setX(node, x) {
|
||||||
|
var cntr = p.reader.dom.find(k.CLS.container);
|
||||||
|
x = Math.min(cntr.offsetWidth - node.offsetWidth, x);
|
||||||
|
x = Math.max(x, 0);
|
||||||
|
Monocle.Styles.setX(node, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createControlElements(holder) {
|
||||||
|
var cntr = holder.dom.make('div', k.CLS.container);
|
||||||
|
var track = cntr.dom.append('div', k.CLS.track);
|
||||||
|
var needleTrail = cntr.dom.append('div', k.CLS.trail);
|
||||||
|
var needle = cntr.dom.append('div', k.CLS.needle);
|
||||||
|
var bubble = cntr.dom.append('div', k.CLS.bubble);
|
||||||
|
|
||||||
|
var cntrListeners, bodyListeners;
|
||||||
|
|
||||||
|
var moveEvt = function (evt, x) {
|
||||||
|
evt.preventDefault();
|
||||||
|
x = (typeof x == "number") ? x : evt.m.registrantX;
|
||||||
|
var place = pixelToPlace(x, cntr);
|
||||||
|
setX(needle, x - needle.offsetWidth / 2);
|
||||||
|
var book = p.reader.getBook();
|
||||||
|
var chps = book.chaptersForComponent(place.componentId);
|
||||||
|
var cmptIndex = p.componentIds.indexOf(place.componentId);
|
||||||
|
var chp = chps[Math.floor(chps.length * place.percentageThrough)];
|
||||||
|
if (cmptIndex > -1 && book.properties.components[cmptIndex]) {
|
||||||
|
var actualPlace = Monocle.Place.FromPercentageThrough(
|
||||||
|
book.properties.components[cmptIndex],
|
||||||
|
place.percentageThrough
|
||||||
|
);
|
||||||
|
chp = actualPlace.chapterInfo() || chp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chp) {
|
||||||
|
bubble.innerHTML = chp.title;
|
||||||
|
}
|
||||||
|
setX(bubble, x - bubble.offsetWidth / 2);
|
||||||
|
|
||||||
|
p.lastX = x;
|
||||||
|
return place;
|
||||||
|
}
|
||||||
|
|
||||||
|
var endEvt = function (evt) {
|
||||||
|
var place = moveEvt(evt, p.lastX);
|
||||||
|
p.reader.moveTo({
|
||||||
|
percent: place.percentageThrough,
|
||||||
|
componentId: place.componentId
|
||||||
|
});
|
||||||
|
Monocle.Events.deafenForContact(cntr, cntrListeners);
|
||||||
|
Monocle.Events.deafenForContact(document.body, bodyListeners);
|
||||||
|
bubble.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
var startFn = function (evt) {
|
||||||
|
bubble.style.display = "block";
|
||||||
|
moveEvt(evt);
|
||||||
|
cntrListeners = Monocle.Events.listenForContact(
|
||||||
|
cntr,
|
||||||
|
{ move: moveEvt }
|
||||||
|
);
|
||||||
|
bodyListeners = Monocle.Events.listenForContact(
|
||||||
|
document.body,
|
||||||
|
{ end: endEvt }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Monocle.Events.listenForContact(cntr, { start: startFn });
|
||||||
|
|
||||||
|
return cntr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
API.createControlElements = createControlElements;
|
||||||
|
API.updateNeedles = updateNeedles;
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
return API;
|
||||||
|
}
|
||||||
|
|
||||||
|
Monocle.Controls.Scrubber.CLS = {
|
||||||
|
container: 'controls_scrubber_container',
|
||||||
|
track: 'controls_scrubber_track',
|
||||||
|
needle: 'controls_scrubber_needle',
|
||||||
|
trail: 'controls_scrubber_trail',
|
||||||
|
bubble: 'controls_scrubber_bubble'
|
||||||
|
}
|
||||||
|
;
|
||||||
|
Monocle.Controls.Spinner = function (reader) {
|
||||||
|
|
||||||
|
var API = { constructor: Monocle.Controls.Spinner }
|
||||||
|
var k = API.constants = API.constructor;
|
||||||
|
var p = API.properties = {
|
||||||
|
reader: reader,
|
||||||
|
divs: [],
|
||||||
|
repeaters: {},
|
||||||
|
showForPages: []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createControlElements(cntr) {
|
||||||
|
var anim = cntr.dom.make('div', 'controls_spinner_anim');
|
||||||
|
anim.dom.append('div', 'controls_spinner_inner');
|
||||||
|
p.divs.push(anim);
|
||||||
|
return anim;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function registerSpinEvent(startEvtType, stopEvtType) {
|
||||||
|
var label = startEvtType;
|
||||||
|
p.reader.listen(startEvtType, function (evt) { spin(label, evt) });
|
||||||
|
p.reader.listen(stopEvtType, function (evt) { spun(label, evt) });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Registers spin/spun event handlers for certain time-consuming events.
|
||||||
|
//
|
||||||
|
function listenForUsualDelays() {
|
||||||
|
registerSpinEvent('monocle:componentloading', 'monocle:componentloaded');
|
||||||
|
registerSpinEvent('monocle:componentchanging', 'monocle:componentchange');
|
||||||
|
registerSpinEvent('monocle:resizing', 'monocle:resize');
|
||||||
|
registerSpinEvent('monocle:jumping', 'monocle:jump');
|
||||||
|
registerSpinEvent('monocle:recalculating', 'monocle:recalculated');
|
||||||
|
p.reader.listen('monocle:notfound', forceSpun);
|
||||||
|
p.reader.listen('monocle:componentfailed', forceSpun);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Displays the spinner. Both arguments are optional.
|
||||||
|
//
|
||||||
|
function spin(label, evt) {
|
||||||
|
label = label || k.GENERIC_LABEL;
|
||||||
|
p.repeaters[label] = true;
|
||||||
|
p.reader.showControl(API);
|
||||||
|
|
||||||
|
// If the delay is on a page other than the page we've been assigned to,
|
||||||
|
// don't show the animation. p.global ensures that if an event affects
|
||||||
|
// all pages, the animation is always shown, even if other events in this
|
||||||
|
// spin cycle are page-specific.
|
||||||
|
var page = (evt && evt.m && evt.m.page) ? evt.m.page : null;
|
||||||
|
if (page && p.divs.length > 1) {
|
||||||
|
p.showForPages[page.m.pageIndex] = true;
|
||||||
|
} else {
|
||||||
|
p.global = true;
|
||||||
|
p.reader.dispatchEvent('monocle:modal:on');
|
||||||
|
}
|
||||||
|
for (var i = 0; i < p.divs.length; ++i) {
|
||||||
|
var show = (p.global || p.showForPages[i]) ? true : false;
|
||||||
|
p.divs[i].dom[show ? 'removeClass' : 'addClass']('dormant');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Stops displaying the spinner. Both arguments are optional.
|
||||||
|
//
|
||||||
|
function spun(label, evt) {
|
||||||
|
label = label || k.GENERIC_LABEL;
|
||||||
|
p.repeaters[label] = false;
|
||||||
|
for (var l in p.repeaters) {
|
||||||
|
if (p.repeaters[l]) { return; }
|
||||||
|
}
|
||||||
|
forceSpun();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function forceSpun() {
|
||||||
|
if (p.global) { p.reader.dispatchEvent('monocle:modal:off'); }
|
||||||
|
p.global = false;
|
||||||
|
p.repeaters = {};
|
||||||
|
p.showForPages = [];
|
||||||
|
for (var i = 0; i < p.divs.length; ++i) {
|
||||||
|
p.divs[i].dom.addClass('dormant');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
API.createControlElements = createControlElements;
|
||||||
|
API.registerSpinEvent = registerSpinEvent;
|
||||||
|
API.listenForUsualDelays = listenForUsualDelays;
|
||||||
|
API.spin = spin;
|
||||||
|
API.spun = spun;
|
||||||
|
API.forceSpun = forceSpun;
|
||||||
|
|
||||||
|
return API;
|
||||||
|
}
|
||||||
|
|
||||||
|
Monocle.Controls.Spinner.GENERIC_LABEL = "generic";
|
||||||
|
Monocle.Controls.Stencil = function (reader, behaviorClasses) {
|
||||||
|
|
||||||
|
var API = { constructor: Monocle.Controls.Stencil }
|
||||||
|
var k = API.constants = API.constructor;
|
||||||
|
var p = API.properties = {
|
||||||
|
reader: reader,
|
||||||
|
behaviors: [],
|
||||||
|
components: {},
|
||||||
|
masks: []
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create the stencil container and listen for draw/update events.
|
||||||
|
//
|
||||||
|
function createControlElements(holder) {
|
||||||
|
behaviorClasses = behaviorClasses || k.DEFAULT_BEHAVIORS;
|
||||||
|
for (var i = 0, ii = behaviorClasses.length; i < ii; ++i) {
|
||||||
|
addBehavior(behaviorClasses[i]);
|
||||||
|
}
|
||||||
|
p.container = holder.dom.make('div', k.CLS.container);
|
||||||
|
p.reader.listen('monocle:turning', hide);
|
||||||
|
p.reader.listen('monocle:turn:cancel', show);
|
||||||
|
p.reader.listen('monocle:turn', update);
|
||||||
|
p.reader.listen('monocle:stylesheetchange', update);
|
||||||
|
p.reader.listen('monocle:resize', update);
|
||||||
|
update();
|
||||||
|
return p.container;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Pass this method an object that responds to 'findElements(doc)' with
|
||||||
|
// an array of DOM elements for that document, and to 'fitMask(elem, mask)'.
|
||||||
|
//
|
||||||
|
// After you have added all your behaviors this way, you would typically
|
||||||
|
// call update() to make them take effect immediately.
|
||||||
|
//
|
||||||
|
function addBehavior(bhvrClass) {
|
||||||
|
var bhvr = new bhvrClass(API);
|
||||||
|
if (typeof bhvr.findElements != 'function') {
|
||||||
|
console.warn('Missing "findElements" method for behavior: %o', bhvr);
|
||||||
|
}
|
||||||
|
if (typeof bhvr.fitMask != 'function') {
|
||||||
|
console.warn('Missing "fitMask" method for behavior: %o', bhvr);
|
||||||
|
}
|
||||||
|
p.behaviors.push(bhvr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Resets any pre-calculated rectangles for the active component,
|
||||||
|
// recalculates them, and forces masks to be "drawn" (moved into the new
|
||||||
|
// rectangular locations).
|
||||||
|
//
|
||||||
|
function update() {
|
||||||
|
var visPages = p.reader.visiblePages();
|
||||||
|
if (!visPages || !visPages.length) { return; }
|
||||||
|
var pageDiv = visPages[0];
|
||||||
|
var cmptId = pageComponentId(pageDiv);
|
||||||
|
if (!cmptId) { return; }
|
||||||
|
p.components[cmptId] = null;
|
||||||
|
calculateRectangles(pageDiv);
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
p.container.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function show() {
|
||||||
|
p.container.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Removes any existing masks.
|
||||||
|
function clear() {
|
||||||
|
while (p.container.childNodes.length) {
|
||||||
|
p.container.removeChild(p.container.lastChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Aligns the stencil container to the shape of the page, then moves the
|
||||||
|
// masks to sit above any currently visible rectangles.
|
||||||
|
//
|
||||||
|
function draw() {
|
||||||
|
var pageDiv = p.reader.visiblePages()[0];
|
||||||
|
var cmptId = pageComponentId(pageDiv);
|
||||||
|
if (!p.components[cmptId]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position the container.
|
||||||
|
alignToComponent(pageDiv);
|
||||||
|
|
||||||
|
// Clear old masks.
|
||||||
|
clear();
|
||||||
|
|
||||||
|
// Layout the masks.
|
||||||
|
if (!p.disabled) {
|
||||||
|
show();
|
||||||
|
var rects = p.components[cmptId];
|
||||||
|
if (rects && rects.length) {
|
||||||
|
layoutRectangles(pageDiv, rects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Iterate over all the <a> elements in the active component, and
|
||||||
|
// create an array of rectangular points corresponding to their positions.
|
||||||
|
//
|
||||||
|
function calculateRectangles(pageDiv) {
|
||||||
|
var cmptId = pageComponentId(pageDiv);
|
||||||
|
if (!p.components[cmptId]) {
|
||||||
|
p.components[cmptId] = [];
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var doc = pageDiv.m.activeFrame.contentDocument;
|
||||||
|
var offset = getOffset(pageDiv);
|
||||||
|
|
||||||
|
for (var b = 0, bb = p.behaviors.length; b < bb; ++b) {
|
||||||
|
var bhvr = p.behaviors[b];
|
||||||
|
var elems = bhvr.findElements(doc);
|
||||||
|
for (var i = 0; i < elems.length; ++i) {
|
||||||
|
var elem = elems[i];
|
||||||
|
if (elem.getClientRects) {
|
||||||
|
var r = elem.getClientRects();
|
||||||
|
for (var j = 0; j < r.length; j++) {
|
||||||
|
p.components[cmptId].push({
|
||||||
|
element: elem,
|
||||||
|
behavior: bhvr,
|
||||||
|
left: Math.ceil(r[j].left + offset.l),
|
||||||
|
top: Math.ceil(r[j].top),
|
||||||
|
width: Math.floor(r[j].width),
|
||||||
|
height: Math.floor(r[j].height)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.components[cmptId];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Update location of visible rectangles - creating as required.
|
||||||
|
//
|
||||||
|
function layoutRectangles(pageDiv, rects) {
|
||||||
|
var offset = getOffset(pageDiv);
|
||||||
|
var visRects = [];
|
||||||
|
for (var i = 0; i < rects.length; ++i) {
|
||||||
|
if (rectVisible(rects[i], offset.l, offset.l + offset.w)) {
|
||||||
|
visRects.push(rects[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < visRects.length; ++i) {
|
||||||
|
var r = visRects[i];
|
||||||
|
var cr = {
|
||||||
|
left: r.left - offset.l,
|
||||||
|
top: r.top,
|
||||||
|
width: r.width,
|
||||||
|
height: r.height
|
||||||
|
};
|
||||||
|
var mask = createMask(r.element, r.behavior);
|
||||||
|
mask.dom.setStyles({
|
||||||
|
display: 'block',
|
||||||
|
left: cr.left+"px",
|
||||||
|
top: cr.top+"px",
|
||||||
|
width: cr.width+"px",
|
||||||
|
height: cr.height+"px",
|
||||||
|
position: 'absolute'
|
||||||
|
});
|
||||||
|
mask.stencilRect = cr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Find the offset position in pixels from the left of the current page.
|
||||||
|
//
|
||||||
|
function getOffset(pageDiv) {
|
||||||
|
return {
|
||||||
|
l: pageDiv.m.offset || 0,
|
||||||
|
w: pageDiv.m.dimensions.properties.width
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Is this area presently on the screen?
|
||||||
|
//
|
||||||
|
function rectVisible(rect, l, r) {
|
||||||
|
return rect.left >= l && rect.left < r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns the active component id for the given page, or the current
|
||||||
|
// page if no argument passed in.
|
||||||
|
//
|
||||||
|
function pageComponentId(pageDiv) {
|
||||||
|
pageDiv = pageDiv || p.reader.visiblePages()[0];
|
||||||
|
if (!pageDiv.m.activeFrame.m.component) { return; }
|
||||||
|
return pageDiv.m.activeFrame.m.component.properties.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Positions the stencil container over the active frame.
|
||||||
|
//
|
||||||
|
function alignToComponent(pageDiv) {
|
||||||
|
cmpt = pageDiv.m.activeFrame.parentNode;
|
||||||
|
p.container.dom.setStyles({
|
||||||
|
left: cmpt.offsetLeft+"px",
|
||||||
|
top: cmpt.offsetTop+"px"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function createMask(element, bhvr) {
|
||||||
|
var mask = p.container.dom.append(bhvr.maskTagName || 'div', k.CLS.mask);
|
||||||
|
Monocle.Events.listenForContact(mask, {
|
||||||
|
start: function () { p.reader.dispatchEvent('monocle:magic:halt'); },
|
||||||
|
move: function (evt) { evt.preventDefault(); },
|
||||||
|
end: function () { p.reader.dispatchEvent('monocle:magic:init'); }
|
||||||
|
});
|
||||||
|
bhvr.fitMask(element, mask);
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Make the active masks visible (by giving them a class -- override style
|
||||||
|
// in monoctrl.css).
|
||||||
|
//
|
||||||
|
function toggleHighlights() {
|
||||||
|
var cls = k.CLS.highlights;
|
||||||
|
if (p.container.dom.hasClass(cls)) {
|
||||||
|
p.container.dom.removeClass(cls);
|
||||||
|
} else {
|
||||||
|
p.container.dom.addClass(cls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function disable() {
|
||||||
|
p.disabled = true;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function enable() {
|
||||||
|
p.disabled = false;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function filterElement(elem, behavior) {
|
||||||
|
if (typeof behavior.filterElement == 'function') {
|
||||||
|
return behavior.filterElement(elem);
|
||||||
|
}
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function maskAssigned(elem, mask, behavior) {
|
||||||
|
if (typeof behavior.maskAssigned == 'function') {
|
||||||
|
return behavior.maskAssigned(elem, mask);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
API.createControlElements = createControlElements;
|
||||||
|
API.addBehavior = addBehavior;
|
||||||
|
API.draw = draw;
|
||||||
|
API.update = update;
|
||||||
|
API.toggleHighlights = toggleHighlights;
|
||||||
|
|
||||||
|
return API;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Monocle.Controls.Stencil.CLS = {
|
||||||
|
container: 'controls_stencil_container',
|
||||||
|
mask: 'controls_stencil_mask',
|
||||||
|
highlights: 'controls_stencil_highlighted'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Monocle.Controls.Stencil.Links = function (stencil) {
|
||||||
|
var API = { constructor: Monocle.Controls.Stencil.Links }
|
||||||
|
|
||||||
|
// Optionally specify the HTML tagname of the mask.
|
||||||
|
API.maskTagName = 'a';
|
||||||
|
|
||||||
|
// Returns an array of all the elements in the given doc that should
|
||||||
|
// be covered with a stencil mask for interactivity.
|
||||||
|
//
|
||||||
|
// (Hint: doc.querySelectorAll() is your friend.)
|
||||||
|
//
|
||||||
|
API.findElements = function (doc) {
|
||||||
|
return doc.querySelectorAll('a[href]');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Return an element. It should usually be a child of the container element,
|
||||||
|
// with a className of the given maskClass. You set up the interactivity of
|
||||||
|
// the mask element here.
|
||||||
|
//
|
||||||
|
API.fitMask = function (link, mask) {
|
||||||
|
var hrefObject = deconstructHref(link);
|
||||||
|
|
||||||
|
if (hrefObject.internal) {
|
||||||
|
mask.setAttribute('href', 'javascript:"Skip to chapter"');
|
||||||
|
mask.onclick = function (evt) {
|
||||||
|
stencil.properties.reader.skipToChapter(hrefObject.internal);
|
||||||
|
evt.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mask.setAttribute('href', hrefObject.external);
|
||||||
|
mask.setAttribute('target', '_blank');
|
||||||
|
mask.onclick = function (evt) { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
link.onclick = function (evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns an object with either:
|
||||||
|
//
|
||||||
|
// - an 'external' property -- an absolute URL with a protocol,
|
||||||
|
// host & etc, which should be treated as an external resource (eg,
|
||||||
|
// open in new window)
|
||||||
|
//
|
||||||
|
// OR
|
||||||
|
//
|
||||||
|
// - an 'internal' property -- a relative URL (with optional hash anchor),
|
||||||
|
// that is treated as a link to component in the book
|
||||||
|
//
|
||||||
|
// A weird but useful property of <a> tags is that while
|
||||||
|
// link.getAttribute('href') will return the actual string value of the
|
||||||
|
// attribute (eg, 'foo.html'), link.href will return the absolute URL (eg,
|
||||||
|
// 'http://example.com/monocles/foo.html').
|
||||||
|
//
|
||||||
|
function deconstructHref(elem) {
|
||||||
|
var loc = document.location;
|
||||||
|
var origin = loc.protocol+'//'+loc.host;
|
||||||
|
var href = elem.href;
|
||||||
|
var path = href.substring(origin.length);
|
||||||
|
var ext = { external: href };
|
||||||
|
|
||||||
|
// Anchor tags with 'target' attributes are always external URLs.
|
||||||
|
if (elem.getAttribute('target')) {
|
||||||
|
return ext;
|
||||||
|
}
|
||||||
|
// URLs with a different protocol or domain are always external.
|
||||||
|
//console.log("Domain test: %s <=> %s", origin, href);
|
||||||
|
if (href.indexOf(origin) != 0) {
|
||||||
|
return ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it is in a sub-path of the current path, it's internal.
|
||||||
|
var topPath = loc.pathname.replace(/[^\/]*\.[^\/]+$/,'');
|
||||||
|
if (topPath[topPath.length - 1] != '/') {
|
||||||
|
topPath += '/';
|
||||||
|
}
|
||||||
|
//console.log("Sub-path test: %s <=> %s", topPath, path);
|
||||||
|
if (path.indexOf(topPath) == 0) {
|
||||||
|
return { internal: path.substring(topPath.length) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a root-relative URL and it's in our list of component ids,
|
||||||
|
// it's internal.
|
||||||
|
var cmptIds = stencil.properties.reader.getBook().properties.componentIds;
|
||||||
|
for (var i = 0, ii = cmptIds.length; i < ii; ++i) {
|
||||||
|
//console.log("Component test: %s <=> %s", cmptIds[i], path);
|
||||||
|
if (path.indexOf(cmptIds[i]) == 0) {
|
||||||
|
return { internal: path }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise it's external.
|
||||||
|
return ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return API;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Monocle.Controls.Stencil.DEFAULT_BEHAVIORS = [Monocle.Controls.Stencil.Links];
|
195
resources/monocle/styles/monocore.css
Normal file
195
resources/monocle/styles/monocore.css
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
/*===========================================================================
|
||||||
|
|
||||||
|
This is a base-level Monocle stylesheet. It assumes no class-prefix has been
|
||||||
|
given to the Reader during initialisation - if one has, you can copy and
|
||||||
|
modify this stylesheet accordingly.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* The reader object that holds pretty much everything.
|
||||||
|
* (A direct child of the element passed to reader initialisation). */
|
||||||
|
|
||||||
|
div.monelem_container {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The div that mimics a leaf of paper in a book. */
|
||||||
|
div.monelem_page {
|
||||||
|
background: white;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 3px;
|
||||||
|
right: 5px;
|
||||||
|
border-right: 1px solid #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The div within the page that determines page margins. */
|
||||||
|
div.monelem_sheaf {
|
||||||
|
top: 1em;
|
||||||
|
left: 1em;
|
||||||
|
bottom: 1em;
|
||||||
|
right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The iframe within the page that loads the content of the book. */
|
||||||
|
div.monelem_component {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* A panel that sits above the entire reader object, holding controls. */
|
||||||
|
div.monelem_overlay {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* A full-size panel to display an announcement (iframe or div) */
|
||||||
|
div.monelem_billboard_container {
|
||||||
|
background: #FFF;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 2000;
|
||||||
|
-webkit-transform: scale(0);
|
||||||
|
-moz-transform: scale(0);
|
||||||
|
transform: scale(0);
|
||||||
|
-webkit-transform-origin: -0 -0;
|
||||||
|
-moz-transform-origin: -0 -0;
|
||||||
|
transform-origin: -0 -0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monelem_billboard_inner {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
overflow: auto;
|
||||||
|
/*-webkit-overflow-scrolling: touch;*/ /* This is sexy, but crashy. */
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_billboard_inner {
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-pack: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
div.monelem_billboard_close {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 50px;
|
||||||
|
height: 30px;
|
||||||
|
color: white;
|
||||||
|
background: #C00;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
text-shadow: 1px 1px 1px #900;
|
||||||
|
font: 9pt Helvetica Neue, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_billboard_close:after {
|
||||||
|
display: block;
|
||||||
|
content: 'Close';
|
||||||
|
width: 100%;
|
||||||
|
line-height: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_book_fatality {
|
||||||
|
font-family: Helvetica Neue, Helvetica, sans-serif;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_book_fatality p {
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
PANELS
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
.monelem_panels_imode_panel {
|
||||||
|
background: rgba(255,255,255,0.7);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monelem_panels_imode_backwardsPanel {
|
||||||
|
-webkit-box-shadow: 1px 1px 3px #777;
|
||||||
|
-moz-box-shadow: 1px 1px 3px #777;
|
||||||
|
box-shadow: 1px 1px 3px #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monelem_panels_imode_forwardsPanel {
|
||||||
|
-webkit-box-shadow: -1px 1px 3px #777;
|
||||||
|
-moz-box-shadow: -1px 1px 3px #777;
|
||||||
|
box-shadow: -1px 1px 3px #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monelem_panels_imode_centralPanel {
|
||||||
|
}
|
||||||
|
|
||||||
|
.monelem_panels_imode_toggleIcon {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If you modify this you could significantly change the way panels work. */
|
||||||
|
div.monelem_controls_panel_expanded {
|
||||||
|
left: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
z-index: 1001 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
Flippers
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
div.monelem_flippers_slider_wait {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
width: 92px;
|
||||||
|
height: 112px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
-webkit-background-size: 100%;
|
||||||
|
-moz-background-size: 100%;
|
||||||
|
background-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 640px) {
|
||||||
|
div.monelem_flippers_slider_wait {
|
||||||
|
width: 61px;
|
||||||
|
height: 75px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
DATA URIs
|
||||||
|
|
||||||
|
These are data-uri packed images, inlined for loading speed and simplicity.
|
||||||
|
Placed at the end of this file because they're visually noisy...
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
div.monelem_panels_imode_toggleIcon {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAaCAYAAABPY4eKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1%2B%2FAAAABV0RVh0Q3JlYXRpb24gVGltZQAzMC82LzEwBMfmVwAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNXG14zYAAANYSURBVEiJtdZbiNVVFMfxj8cx85JkIGlqSESgOGA9WIQgGmTRUyRaYFJDnUWYGV2eyiCpkIbEKJI1UqYvUkmFDxFBgpghonajSDCM7hcxLSnt4ulh%2F2c4HufMTOH8Xs75%2F%2Ffa67v3%2Bu%2B91hphGJWZNUzCXJyKiHd6xxqNhhGDTB6NOViAyzARY3EaP%2BNL7MCBiPi9Ze4leBlTsR9jcCnuiYgDbeGZeV4F7EINe7EP3%2BJ49W4GrsZ8NPAGXouIk5k5F93YFhHPVT5H4kbcjaX1ev3kWfDMPB9P4ko8ERE7BopONWcOVmMc1uBRrG8Oc5Ptq1hdr9cPdrQMTMUWfBQRCweD9ioiPsQtmbkeu7G8P3ClsZSI98EzcxqeUsLXM1RwZs7ErRiJKXgQN2Tmzoj4qsV2Hn7BYcq369UaHIqI5yPizyGCx2MPfsRVOBoR6%2FA%2BNmXmqCbbm%2FAiMiJO9cEzcwEuwLODwMZk5oXVLYA6PouIF%2FC6cvBgI37D0mreStyJroh4r9df785XYGtEHG8Hfnjb1w08Xu2qq3regtOZuaka2whV5NZieWY%2BhkV4ICJ2N%2FusZeYMJQm8NdCuuxdPH4HENGzsXjx9REQcqRxvR2dEfNBrHxF7lHywGPXW7085cEvwZkScHAheaRz%2BwngcqyAnlEPan%2Fbh5oj4rr%2FBDlyOXUMA%2Fx%2F9oFytM5SZs3t6epbWlOtxeJjg%2BzEmMye3vF%2BCYx2YhdFnTTs3OoQT2JqZ3TiC2zETyzrwrnIwhkMTqwVsxW24GLsiYmWj0dCBo2gNy7nSRfgpIjZjM6WU1ut1lHt%2BGLOHCd6J79sN1pSkMSUzJwwD%2FBoD5I9aRHyiFIVFQ3D2j1KR%2Fh7MMDPnY1JE7GwLr3434N5BnI3GFRiFzuai0Ub34aWBDGr0pcKPM%2FPpqovpT11KoVinNAvXt1lkLTNXKFesXU1HUz3HI0plWqW0QGcoIjYoERpMy7AS17b2da06o43KzLF4RanRzwwx3%2FfOHYW7lL5ubUR83p9do9Ho%2B99fDzcZDynfdxPejog%2FBoCOxHW4AxOwKiK%2BaGc%2FILzJ6ULcXznciwM4qFSzCUob3Km0UCeU3W5v5%2B8%2FwZsWMQvzlN1Nq8C%2F4ht8qkRm72B%2B%2BoP%2FC0sEOftJmUbfAAAAAElFTkSuQmCC);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_flippers_slider_wait {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFwAAABwCAMAAACkX%2BcnAAAB0VBMVEUAAACDg4OEhISFhYWGhoaHh4eIiIiJiIiJiYmKioqLi4uMjIyNjY2Ojo6Pj4%2BQkJCRkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqbm5ucnJycnJ2dnZ2dnZ6dnZ%2Benp6enp%2Bfn5%2Bfn6CgoKCgoKGhoKChoaGioqKjo6OkpKSlpaWmpaWmpqaoqKiqqqqrq6usrKytra2urq6wsLCxsbGzs7O0tLS0tLW1tbW1tba1tbe2tri4uLi4uLm4uLq6ury7u7y8vLy8vL28vL%2B9vb2%2Bvr6%2Bvr%2B%2Fv7%2B%2Fv8HAwMDAwMLAwMPBwcPCwsPExMTExMXFxcXGxsbHx8fIyMjJycrOztDOztHPz9DPz9HR0dTS0tTT09TT09XU1NbU1NfV1dfW1tjW1tnX19fX19rY2Nra2tva2tzd3eDe3t7f39%2Fh4eHi4uLl5enn5%2Bnp6ezp6e3q6u3q6u7r6%2B7r6%2B%2Fs7O%2Fs7PDt7fDt7fHu7vHu7vLv7%2B%2Fv7%2FLv7%2FPw8PDw8PPw8PTx8fTx8fXy8vXy8vbz8%2Fbz8%2Ff09Pf09Pj19fj19fn29vn39%2Fn39%2Fr4%2BPr4%2BPv5%2Bfv6%2Bvv6%2Bvz7%2B%2Fz7%2B%2F38%2FP39%2Ff39%2Ff7%2B%2Fv7%2B%2Fv%2F%2F%2F%2F%2BHSJEZAAAAAXRSTlMAQObYZgAAA5dJREFUaN61lk1uE0EQhd%2BrsQlREAgkFkQKLJByteQU3IIdd2OBYIFASFmAFLurWPT0uOfXme6aWUXy6PNL9XPXR3z6DSI93wQ0GkHjzweapM%2B%2Btn8SMAERPzKQQKN7IDRhD2APgkbumucvXp24T3s%2BH47H7%2F9U1AxmpvaDzV5IUMBfD0CbQXYPly93K%2BEiwneqphpMVc3e7p492zciQhGKNN2bX%2F42shJOEQFIQgAKgfgdpvFz7d58%2FPO4Fn5PiggBAUkAYhoUMJipwU5vhsfjWjhESMTsBChQVVMDYICadfjD4VAAFyGYZVcN7Vzar4iP6frkd5RuLjG7WlCFwdSy4ICtPlBAKJLNhYBq6HKf8IHrx4J7IQX5maqFLHeC3yrWwyEiFACSzlTVVFNuzQZTAG%2BrLoQwVT1kubvGF4wlVj2vi2isuvWrbiXJIUISYKwL5qpuWgbvXQHxSCeqbiXwvOrpClC1QdXViuAQUnpXgE1U%2FSb%2BUwVVF7JfdTWN2G4uFyiaeZz6oOpB1drzTF0sSw6ySdc5Y%2FZe1SPeCpPfS6p6yq4arK16V5eyAwWEp6oTEKpqewXEygBW9iMabzsAZjqoOkuTL227tjJvSg8UaG%2FGhW33obSK8d4dVj1eAV3VrXQsuBtXvd12XdWteCxg2nbobbuU2xQsHst42zHe6lllypOnbcdUeZ62HUzNoOXJz4vdpZXDz4rde5TDz4rdsQ6%2BLHZNxVjOip3VJD8ndjVtOSt2rEp%2BRuxCHXxZ7G6tCr4sdhUX1xPETmvhC2KndWNZFjtUjmVR7KRyLItiF2qTL4ndtdXCF8Tuqhq%2BIHaonfmi2Ek1fEHsQjV8YdtVt2VR7DzgM2J36QCfFbsbB%2Fi82MEBPit2HvBZsfMYy6zYuSSfq7oLfE7sLpzgk2J37QKfETt1gc%2BJnQ98Rux84NNiJ07wSbELTvBpsXOCT4rdRz%2F4WOzMCz4pdl7wKbGDG3xC7NzGMiV2jvCx2PnNfELsbvzgY7FrHOFjsXOEj7YdHeFjsfOF96sePOFjsXOED8XutSt8sO2uXOFDsfOFD6ruCx9U3Rc%2BEDt3eC52zvC%2B2DnD%2B2LnDe9V3RveEzt3eC527vBc7NzhudhtAe%2BuAH94VnV%2FeCZ2G8BzscMmUxdgi5lnYrcF%2FCR2wCZHSvftP9x2m8DTttsEnsRuK7hs8%2FPPxG4beCt2G8HbbbcNPG67reAUEfwHRePBMkvuZ4wAAAAASUVORK5CYII%3D);
|
||||||
|
}
|
169
resources/monocle/styles/monoctrl.css
Normal file
169
resources/monocle/styles/monoctrl.css
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
/*===========================================================================
|
||||||
|
CONTROLS
|
||||||
|
|
||||||
|
The standard Monocle stylesheet for the optional Monocle controls. See
|
||||||
|
comments for monocore.css, which apply here too.
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Contents */
|
||||||
|
|
||||||
|
div.monelem_controls_contents_container {
|
||||||
|
position: absolute;
|
||||||
|
width: 75%;
|
||||||
|
height: 75%;
|
||||||
|
left: 12.5%;
|
||||||
|
top: 12.5%;
|
||||||
|
background: #EEE;
|
||||||
|
border: 2px solid #F7F7F7;
|
||||||
|
border-radius: 9px;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
-moz-border-radius: 9px;
|
||||||
|
-webkit-border-radius: 9px;
|
||||||
|
box-shadow: 1px 2px 6px rgba(0,0,0,0.5);
|
||||||
|
-moz-box-shadow: 1px 2px 6px rgba(0,0,0,0.5);
|
||||||
|
-webkit-box-shadow: 1px 2px 6px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
ol.monelem_controls_contents_list {
|
||||||
|
margin: 6px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.monelem_controls_contents_chapter {
|
||||||
|
list-style: none;
|
||||||
|
line-height: 220%;
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-right: 2em;
|
||||||
|
border-bottom: 2px groove #FEFEFE;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.monelem_controls_contents_chapter_active {
|
||||||
|
background: #999;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Magnifier */
|
||||||
|
|
||||||
|
.monelem_controls_magnifier_button {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #555;
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
right: 10px;
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monelem_controls_magnifier_a {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monelem_controls_magnifier_A {
|
||||||
|
font-size: 18px;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Spinner */
|
||||||
|
|
||||||
|
.monelem_controls_spinner_anim {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: white;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
.monelem_controls_spinner_anim.monelem_dormant {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Scrubber */
|
||||||
|
|
||||||
|
div.monelem_controls_scrubber_container {
|
||||||
|
position: absolute;
|
||||||
|
left: 1em;
|
||||||
|
right: 1em;
|
||||||
|
bottom: 4px;
|
||||||
|
height: 30px;
|
||||||
|
background: rgba(255,255,255,0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_controls_scrubber_track {
|
||||||
|
margin-top: 10px;
|
||||||
|
height: 5px;
|
||||||
|
border: 1px solid #999;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_controls_scrubber_needle {
|
||||||
|
position: absolute;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
top: 5px;
|
||||||
|
background: #CCC;
|
||||||
|
border: 1px solid #999;
|
||||||
|
border-radius: 8px;
|
||||||
|
-moz-border-radius: 8px;
|
||||||
|
-webkit-border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_controls_scrubber_trail {
|
||||||
|
position: absolute;
|
||||||
|
background: #DDD;
|
||||||
|
top: 11px;
|
||||||
|
left: 1px;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_controls_scrubber_bubble {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
padding: 1em;
|
||||||
|
min-width: 20%;
|
||||||
|
max-width: 30%;
|
||||||
|
bottom: 2.5em;
|
||||||
|
background: rgba(0, 0, 0, 0.9);
|
||||||
|
color: #CCC;
|
||||||
|
font: bold 12px Lucida Grande, Tahoma, Helvetica, Arial, sans-serif;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 10px;
|
||||||
|
-moz-border-radius: 10px;
|
||||||
|
-webkit-border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Stencil */
|
||||||
|
div.monelem_controls_stencil_container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.monelem_controls_stencil_mask {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_controls_stencil_highlighted .monelem_controls_stencil_mask {
|
||||||
|
background: rgba(0,0,255,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
DATA URIs
|
||||||
|
|
||||||
|
These are data-uri packed images, inlined for loading speed and simplicity.
|
||||||
|
Placed at the end of this file because they're visually noisy...
|
||||||
|
---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
div.monelem_controls_spinner_anim {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAA0CAMAAAANBM47AAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M1cbXjNgAAABV0RVh0Q3JlYXRpb24gVGltZQAxNy81LzEwnOhoKAAAAE5QTFRFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxKKmWQAAABp0Uk5TAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBl0wLilAAAC8klEQVQYGZXBB2LjOAADQFCimtVFEoD//9HLbrJxipzoZoBToYptUwV8V/Xrsc8RP6i7aduPXHI69mWIAR9UY6Is5rnCuTBsWXeLkijbTFOLf7okW6R8zxEnwphskfIrifJdW4u/RtlpbGLsdjoHfDNkSZTSNg192w3jchSJEtcawCRzDvgjLPINX1SbSSvNXcC7eNuLXpQuTFbp8CZkH/isyS68H0PAF+0iUzxoNni33HPAR51UxDHgRLObslLEw3TPFT7oKPqIeOImURs+WJ0CHlqKXgLOxL4NgyRqxbuqeMNDXURPOBNWSokquRRP+GeVOzwcLlpwJmx3WVJuY2ZRi1ezfOBhdNGGU52ZhrloBzqSucKLerdLxLtIKlc4Nd9LA6wuNTC5aAbQZzs3eFhE9Tg3mw2wqkQgHCZrTJK3iIcoasMTvXX0E30EAK2k+Wbrho8mky2eCLslSz3+2ERKucVHIZsbnqp2WWXEX60ossMnrakeP+jGocabg9SGzyaXHHDRpOIO/zRjDWCTNlzVsLjFm4bODapE33BZoke8mVy8oqXY4rLNXvFmEnXDKJYaly3SjlchkSOwiCngstFMeDXLE4CVygGX3e6FawUgzFIKANbiHHDZ7U4qL7c5SWzxYqFywGXjvVD3F3Zu8ccs5gqXzeYx7CTTWOOvnmTEZZu0ItSxrvAmZrrHZYme8dkhLbiqLkUDPlvMA1cNIiM+613Y4KJNSviiprTgmrrQM75arVzhkllUxFetqBlXVEXa8d0hMeKCxVSH73rRG37XidpxZlXRiN9UhYUtztRFVI+fhUPFE851KlSHn4TNxTueGU2yx3PVbipVeGpxIaeAJ2IynRv8YHEp3iNOjRRdGvxotGjONb7pD7M4RfyiK6ZclhYf1bdDprRW+FW9SZSUlqGtq1BVTTftRaKce1zS7bIpWyW/oK0i38tU4apupWyRsijKVhoj/o+6W45cJEoqaR+bgP8txH5a1nUZ2gq/+Q/51T5MhuG3fQAAAABJRU5ErkJggg==);
|
||||||
|
}
|
|
@ -74,7 +74,7 @@ class EPub {
|
||||||
$spine = $this->xpath->query('//opf:spine')->item(0);
|
$spine = $this->xpath->query('//opf:spine')->item(0);
|
||||||
$tocid = $spine->getAttribute('toc');
|
$tocid = $spine->getAttribute('toc');
|
||||||
$tochref = $this->xpath->query("//opf:manifest/opf:item[@id='$tocid']")->item(0)->attr('href');
|
$tochref = $this->xpath->query("//opf:manifest/opf:item[@id='$tocid']")->item(0)->attr('href');
|
||||||
$tocpath = dirname($this->meta).'/'.$tochref;
|
$tocpath = $this->getFullPath ($tochref);
|
||||||
// read epub toc
|
// read epub toc
|
||||||
if (!$this->zip->FileExists($tocpath)) {
|
if (!$this->zip->FileExists($tocpath)) {
|
||||||
throw new Exception ("Unable to find " . $tocpath);
|
throw new Exception ("Unable to find " . $tocpath);
|
||||||
|
@ -146,7 +146,7 @@ class EPub {
|
||||||
$nodes = $this->xpath->query('//opf:spine/opf:itemref');
|
$nodes = $this->xpath->query('//opf:spine/opf:itemref');
|
||||||
foreach($nodes as $node){
|
foreach($nodes as $node){
|
||||||
$idref = $node->getAttribute('idref');
|
$idref = $node->getAttribute('idref');
|
||||||
$spine[] = $this->xpath->query("//opf:manifest/opf:item[@id='$idref']")->item(0)->getAttribute('href');
|
$spine[] = $this->encodeComponentName ($this->xpath->query("//opf:manifest/opf:item[@id='$idref']")->item(0)->getAttribute('href'));
|
||||||
}
|
}
|
||||||
return $spine;
|
return $spine;
|
||||||
}
|
}
|
||||||
|
@ -155,22 +155,66 @@ class EPub {
|
||||||
* Get the component content
|
* Get the component content
|
||||||
*/
|
*/
|
||||||
public function component($comp) {
|
public function component($comp) {
|
||||||
$path = dirname($this->meta).'/'.$comp;
|
$path = $this->decodeComponentName ($comp);
|
||||||
|
$path = $this->getFullPath ($path);
|
||||||
if (!$this->zip->FileExists($path)) {
|
if (!$this->zip->FileExists($path)) {
|
||||||
throw new Exception ("Unable to find " . $path);
|
throw new Exception ("Unable to find {$path} <{$comp}>");
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->zip->FileRead($path);
|
$data = $this->zip->FileRead($path);
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getComponentName ($comp, $elementPath) {
|
||||||
|
$path = $this->decodeComponentName ($comp);
|
||||||
|
$path = $this->getFullPath ($path, $elementPath);
|
||||||
|
if (!$this->zip->FileExists($path)) {
|
||||||
|
error_log ("Unable to find " . $path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$ref = dirname('/'.$this->meta);
|
||||||
|
$ref = ltrim($ref,'\\');
|
||||||
|
$ref = ltrim($ref,'/');
|
||||||
|
if (strlen ($ref) > 0) {
|
||||||
|
$path = str_replace ($ref . "/", "", $path);
|
||||||
|
}
|
||||||
|
return $this->encodeComponentName ($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode the component name (to replace / and -)
|
||||||
|
*/
|
||||||
|
private function encodeComponentName ($src) {
|
||||||
|
return str_replace (array ("/", "-"),
|
||||||
|
array ("~SLASH~", "~DASH~"),
|
||||||
|
$src);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the component name (to replace / and -)
|
||||||
|
*/
|
||||||
|
private function decodeComponentName ($src) {
|
||||||
|
return str_replace (array ("~SLASH~", "~DASH~"),
|
||||||
|
array ("/", "-"),
|
||||||
|
$src);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the component content type
|
* Get the component content type
|
||||||
*/
|
*/
|
||||||
public function componentContentType($comp) {
|
public function componentContentType($comp) {
|
||||||
|
$comp = $this->decodeComponentName ($comp);
|
||||||
return $this->xpath->query("//opf:manifest/opf:item[@href='$comp']")->item(0)->getAttribute('media-type');
|
return $this->xpath->query("//opf:manifest/opf:item[@href='$comp']")->item(0)->getAttribute('media-type');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getNavPointDetail ($node) {
|
||||||
|
$title = $this->toc_xpath->query('x:navLabel/x:text', $node)->item(0)->nodeValue;
|
||||||
|
$src = $this->toc_xpath->query('x:content', $node)->item(0)->attr('src');
|
||||||
|
$src = $this->decodeComponentName ($src);
|
||||||
|
return array("title" => $title, "src" => $src);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Epub content (TOC) as an array
|
* Get the Epub content (TOC) as an array
|
||||||
*
|
*
|
||||||
|
@ -180,9 +224,12 @@ class EPub {
|
||||||
$contents = array();
|
$contents = array();
|
||||||
$nodes = $this->toc_xpath->query('//x:ncx/x:navMap/x:navPoint');
|
$nodes = $this->toc_xpath->query('//x:ncx/x:navMap/x:navPoint');
|
||||||
foreach($nodes as $node){
|
foreach($nodes as $node){
|
||||||
$title = $this->toc_xpath->query('x:navLabel/x:text', $node)->item(0)->nodeValue;
|
$contents[] = $this->getNavPointDetail ($node);
|
||||||
$src = $this->toc_xpath->query('x:content', $node)->item(0)->attr('src');
|
|
||||||
$contents[] = array("title" => $title, "src" => $src);
|
$insidenodes = $this->toc_xpath->query('x:navPoint', $node);
|
||||||
|
foreach($insidenodes as $insidenode){
|
||||||
|
$contents[] = $this->getNavPointDetail ($insidenode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $contents;
|
return $contents;
|
||||||
}
|
}
|
||||||
|
@ -543,6 +590,53 @@ class EPub {
|
||||||
return $nodes->item(0);
|
return $nodes->item(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function Combine($a, $b)
|
||||||
|
{
|
||||||
|
$isAbsolute = false;
|
||||||
|
if ($a[0] == "/")
|
||||||
|
$isAbsolute = true;
|
||||||
|
|
||||||
|
if ($b[0] == "/")
|
||||||
|
throw new InvalidArgumentException("Second path part must not start with " . $m_Separator);
|
||||||
|
|
||||||
|
$splittedA = split("/", $a);
|
||||||
|
$splittedB = split("/", $b);
|
||||||
|
|
||||||
|
$pathParts = array();
|
||||||
|
$mergedPath = array_merge($splittedA, $splittedB);
|
||||||
|
|
||||||
|
foreach($mergedPath as $item)
|
||||||
|
{
|
||||||
|
if ($item == null || $item == "" || $item == ".")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ($item == "..")
|
||||||
|
{
|
||||||
|
array_pop($pathParts);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push($pathParts, $item);
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = implode("/", $pathParts);
|
||||||
|
if ($isAbsolute)
|
||||||
|
return("/" . $path);
|
||||||
|
else
|
||||||
|
return($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFullPath ($file, $context = NULL) {
|
||||||
|
$path = dirname('/'.$this->meta).'/'.$file;
|
||||||
|
$path = ltrim($path,'\\');
|
||||||
|
$path = ltrim($path,'/');
|
||||||
|
if (!empty ($context)) {
|
||||||
|
$path = $this->combine (dirname ($path), $context);
|
||||||
|
}
|
||||||
|
//error_log ("FullPath : $path ($file / $context)");
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
public function updateForKepub () {
|
public function updateForKepub () {
|
||||||
$item = $this->getCoverItem ();
|
$item = $this->getCoverItem ();
|
||||||
if (!is_null ($item)) {
|
if (!is_null ($item)) {
|
||||||
|
|
273
styles/cops-monocle.css
Normal file
273
styles/cops-monocle.css
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: #000;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#components {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#reader, #readerBg {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#reader div pre {
|
||||||
|
white-space: normal;
|
||||||
|
font: normal 100% serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* from smallest and outermost to largest and innermost */
|
||||||
|
.dummyPage {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 20px;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 15px;
|
||||||
|
background-color: #FCF6F0;
|
||||||
|
-webkit-box-shadow: 2px 2px 4px #754;
|
||||||
|
-moz-box-shadow: 2px 2px 4px #754;
|
||||||
|
box-shadow: 2px 2px 4px #754;
|
||||||
|
-webkit-border-top-left-radius: 26px 6px;
|
||||||
|
-webkit-border-bottom-left-radius: 26px 6px;
|
||||||
|
-moz-border-top-left-radius: 26px 6px;
|
||||||
|
-moz-border-bottom-left-radius: 26px 6px;
|
||||||
|
border-top-left-radius: 26px 6px;
|
||||||
|
border-bottom-left-radius: 26px 6px;
|
||||||
|
}
|
||||||
|
.dummyPage + .dummyPage {
|
||||||
|
top: 16px;
|
||||||
|
bottom: 16px;
|
||||||
|
right: 16px;
|
||||||
|
-webkit-box-shadow: 1px 0 2px #A99;
|
||||||
|
-moz-box-shadow: 1px 0 2px #A99;
|
||||||
|
box-shadow: 1px 0 2px #A99;
|
||||||
|
}
|
||||||
|
.dummyPage + .dummyPage + .dummyPage {
|
||||||
|
top: 13px;
|
||||||
|
bottom: 13px;
|
||||||
|
right: 18px;
|
||||||
|
background-color: #FFF9F4;
|
||||||
|
}
|
||||||
|
.dummyPage + .dummyPage + .dummyPage + .dummyPage {
|
||||||
|
top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 21px;
|
||||||
|
}
|
||||||
|
.dummyPage + .dummyPage + .dummyPage + .dummyPage + .dummyPage {
|
||||||
|
top: 8px;
|
||||||
|
bottom: 8px;
|
||||||
|
right: 25px;
|
||||||
|
}
|
||||||
|
.jacket {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
bottom: 1px;
|
||||||
|
right: 3px;
|
||||||
|
left: 65%;
|
||||||
|
-webkit-box-shadow: -3px 0 3px #311;
|
||||||
|
-moz-box-shadow: -3px 0 3px #311;
|
||||||
|
box-shadow: -3px 0 3px #311;
|
||||||
|
-webkit-border-top-right-radius: 3px;
|
||||||
|
-webkit-border-bottom-right-radius: 3px;
|
||||||
|
-moz-border-top-right-radius: 3px;
|
||||||
|
-moz-border-bottom-right-radius: 3px;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
border-bottom-right-radius: 3px;
|
||||||
|
background-color: #F7F7F7;
|
||||||
|
background: -webkit-linear-gradient(0deg, #DDD, #FFF);
|
||||||
|
background: -moz-linear-gradient(0deg , #DDD, #FFF);
|
||||||
|
background: linear-gradient(90deg, #DDD, #FFF);
|
||||||
|
}
|
||||||
|
.board {
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
bottom: 1px;
|
||||||
|
width: 90%;
|
||||||
|
background-color: #974;
|
||||||
|
border: 1px solid #852;
|
||||||
|
}
|
||||||
|
|
||||||
|
.runner {
|
||||||
|
color: #542;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 82%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pageNumber, .bookTitle, .chapterTitle {
|
||||||
|
padding: 3% 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookTitle, .chapterTitle {
|
||||||
|
position: absolute;
|
||||||
|
top: 1%;
|
||||||
|
left: 6%;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chapterTitle {
|
||||||
|
top: auto;
|
||||||
|
bottom: 1%;
|
||||||
|
right: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pageNumber {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1%;
|
||||||
|
right: 8%;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#readerBg {
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc ul.root {
|
||||||
|
position: absolute;
|
||||||
|
top: 50px;
|
||||||
|
left: 8%;
|
||||||
|
max-height: 75%;
|
||||||
|
max-width: 80%;
|
||||||
|
background: #E0D3C0;
|
||||||
|
-webkit-box-shadow: 1px 2px 2px #652;
|
||||||
|
-moz-box-shadow: 1px 2px 2px #652;
|
||||||
|
-webkit-border-radius: 10px;
|
||||||
|
-moz-border-radius: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-y: auto;
|
||||||
|
color: #432;
|
||||||
|
font: 11pt Georgia, serif;
|
||||||
|
text-shadow: 1px 1px #EEE6D0;
|
||||||
|
border: 1px solid #EED;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tocArrow {
|
||||||
|
position: absolute;
|
||||||
|
top: 40px;
|
||||||
|
left: 16%;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
-webkit-transform: rotateZ(45deg);
|
||||||
|
background: #E0D3C0;
|
||||||
|
z-index: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc li {
|
||||||
|
list-style: none;
|
||||||
|
line-height: 220%;
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-right: 2em;
|
||||||
|
border-bottom: 2px groove #FFF6E9;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc li span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc ul li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toc ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#toc ul.root {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Overrides to core elements */
|
||||||
|
|
||||||
|
div.monelem_container {
|
||||||
|
background: none;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_page {
|
||||||
|
top: 6px;
|
||||||
|
bottom: 6px;
|
||||||
|
right: 4px;
|
||||||
|
border-color: #CBA;
|
||||||
|
outline: none;
|
||||||
|
-webkit-box-shadow: 1px 0 1px #CBA;
|
||||||
|
-moz-box-shadow: 1px 0 1px #CBA;
|
||||||
|
box-shadow: 1px 0 2px #CBA;
|
||||||
|
-webkit-border-top-left-radius: 26px 4px;
|
||||||
|
-webkit-border-bottom-left-radius: 26px 4px;
|
||||||
|
-moz-border-top-left-radius: 26px 4px;
|
||||||
|
-moz-border-bottom-left-radius: 26px 4px;
|
||||||
|
border-top-left-radius: 26px 4px;
|
||||||
|
border-bottom-left-radius: 26px 4px;
|
||||||
|
background-color: #FFFFFE;
|
||||||
|
background-image: -webkit-linear-gradient(0deg, #EDEAE8 0px, #FFFFFE 24px);
|
||||||
|
background-image: -moz-linear-gradient(0deg, #EDEAE8 0px, #FFFFFE 24px);
|
||||||
|
background-image: linear-gradient(90deg, #EDEAE8 0px, #FFFFFE 24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_sheaf {
|
||||||
|
left: 6%;
|
||||||
|
right: 8%;
|
||||||
|
top: 8%;
|
||||||
|
bottom: 8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Overriding magnifier button display */
|
||||||
|
|
||||||
|
div.monelem_controls_magnifier_button {
|
||||||
|
color: #632;
|
||||||
|
padding: 2%;
|
||||||
|
top: 1%;
|
||||||
|
right: 6%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Overriding table of contents display */
|
||||||
|
|
||||||
|
div.monelem_controls_contents_container {
|
||||||
|
background: #E0D3C0;
|
||||||
|
border: 1px solid #EED;
|
||||||
|
font: 11pt Georgia, serif;
|
||||||
|
color: #432;
|
||||||
|
text-shadow: 1px 1px #FFF6E0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_controls_contents_chapter {
|
||||||
|
border-bottom: 2px groove #FFF6E9;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.monelem_controls_contents_chapter_active {
|
||||||
|
text-shadow: -1px -1px #876;
|
||||||
|
background: #BA9;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Overriding the scrubber display */
|
||||||
|
div.monelem_controls_scrubber_container {
|
||||||
|
left: 5.5%;
|
||||||
|
right: 9%;
|
||||||
|
bottom: 2%;
|
||||||
|
background: #FFFEFC;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_controls_scrubber_track {
|
||||||
|
border-color: #432;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_controls_scrubber_needle {
|
||||||
|
border-color: #432;
|
||||||
|
background: #E0D3C0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.monelem_controls_scrubber_trail {
|
||||||
|
background: #E0D3C0;
|
||||||
|
}
|
177
styles/cops-monocle.js
Normal file
177
styles/cops-monocle.js
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
Monocle.DEBUG = true;
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
Monocle.Styles.container.right = "24px";
|
||||||
|
|
||||||
|
// Initialize the reader element.
|
||||||
|
Monocle.Events.listen(
|
||||||
|
window,
|
||||||
|
'load',
|
||||||
|
function () {
|
||||||
|
var readerOptions = {};
|
||||||
|
|
||||||
|
/* PLACE SAVER */
|
||||||
|
var bkTitle = bookData.getMetaData('title');
|
||||||
|
var placeSaver = new Monocle.Controls.PlaceSaver(bkTitle);
|
||||||
|
readerOptions.place = placeSaver.savedPlace();
|
||||||
|
readerOptions.panels = Monocle.Panels.Marginal;
|
||||||
|
readerOptions.stylesheet = "body { " +
|
||||||
|
"color: #210;" +
|
||||||
|
"font-family: Palatino, Georgia, serif;" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
/* Initialize the reader */
|
||||||
|
window.reader = Monocle.Reader(
|
||||||
|
'reader',
|
||||||
|
bookData,
|
||||||
|
readerOptions,
|
||||||
|
function(reader) {
|
||||||
|
reader.addControl(placeSaver, 'invisible');
|
||||||
|
|
||||||
|
/* SPINNER */
|
||||||
|
var spinner = Monocle.Controls.Spinner(reader);
|
||||||
|
reader.addControl(spinner, 'page', { hidden: true });
|
||||||
|
spinner.listenForUsualDelays('reader');
|
||||||
|
|
||||||
|
/* Because the 'reader' element changes size on window resize,
|
||||||
|
* we should notify it of this event. */
|
||||||
|
Monocle.Events.listen(
|
||||||
|
window,
|
||||||
|
'resize',
|
||||||
|
function () { window.reader.resized() }
|
||||||
|
);
|
||||||
|
|
||||||
|
Monocle.Events.listen(window.top.document, 'keyup', function(evt) {
|
||||||
|
var eventCharCode = evt.charCode || evt.keyCode;
|
||||||
|
var dir = null;
|
||||||
|
var flipper = reader.Flipper;
|
||||||
|
if (eventCharCode == 33 || eventCharCode == 37) { // Page down or Left arrow
|
||||||
|
dir = -1;
|
||||||
|
} else if (eventCharCode == 34 || eventCharCode == 39 ) { // Page down or Right arrow
|
||||||
|
dir = 1;
|
||||||
|
}
|
||||||
|
if (dir) {
|
||||||
|
reader.moveTo({ direction: dir });
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* MAGNIFIER CONTROL */
|
||||||
|
var magnifier = new Monocle.Controls.Magnifier(reader);
|
||||||
|
reader.addControl(magnifier, 'page');
|
||||||
|
|
||||||
|
/* The stencil activates internal links */
|
||||||
|
var stencil = new Monocle.Controls.Stencil(reader);
|
||||||
|
reader.addControl(stencil);
|
||||||
|
//stencil.toggleHighlights();
|
||||||
|
|
||||||
|
/* BOOK TITLE RUNNING HEAD */
|
||||||
|
var bookTitle = {}
|
||||||
|
bookTitle.contentsMenu = Monocle.Controls.Contents(reader);
|
||||||
|
reader.addControl(bookTitle.contentsMenu, 'popover', { hidden: true });
|
||||||
|
bookTitle.createControlElements = function () {
|
||||||
|
var cntr = document.createElement('div');
|
||||||
|
cntr.className = "bookTitle";
|
||||||
|
var runner = document.createElement('div');
|
||||||
|
runner.className = "runner";
|
||||||
|
runner.innerHTML = reader.getBook().getMetaData('title');
|
||||||
|
cntr.appendChild(runner);
|
||||||
|
|
||||||
|
Monocle.Events.listenForContact(
|
||||||
|
cntr,
|
||||||
|
{
|
||||||
|
start: function (evt) {
|
||||||
|
if (evt.preventDefault) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
evt.preventDefault();
|
||||||
|
} else {
|
||||||
|
evt.returnValue = false;
|
||||||
|
}
|
||||||
|
reader.showControl(bookTitle.contentsMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return cntr;
|
||||||
|
}
|
||||||
|
reader.addControl(bookTitle, 'page');
|
||||||
|
|
||||||
|
|
||||||
|
/* CHAPTER TITLE RUNNING HEAD */
|
||||||
|
var chapterTitle = {
|
||||||
|
runners: [],
|
||||||
|
createControlElements: function (page) {
|
||||||
|
var cntr = document.createElement('div');
|
||||||
|
cntr.className = "chapterTitle";
|
||||||
|
var runner = document.createElement('div');
|
||||||
|
runner.className = "runner";
|
||||||
|
cntr.appendChild(runner);
|
||||||
|
this.runners.push(runner);
|
||||||
|
this.update(page);
|
||||||
|
return cntr;
|
||||||
|
},
|
||||||
|
update: function (page) {
|
||||||
|
var place = reader.getPlace(page);
|
||||||
|
if (place) {
|
||||||
|
this.runners[page.m.pageIndex].innerHTML = place.chapterTitle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.addControl(chapterTitle, 'page');
|
||||||
|
reader.listen(
|
||||||
|
'monocle:pagechange',
|
||||||
|
function (evt) { chapterTitle.update(evt.m.page); }
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/* PAGE NUMBER RUNNING HEAD */
|
||||||
|
var pageNumber = {
|
||||||
|
runners: [],
|
||||||
|
createControlElements: function (page) {
|
||||||
|
var cntr = document.createElement('div');
|
||||||
|
cntr.className = "pageNumber";
|
||||||
|
var runner = document.createElement('div');
|
||||||
|
runner.className = "runner";
|
||||||
|
cntr.appendChild(runner);
|
||||||
|
this.runners.push(runner);
|
||||||
|
this.update(page, page.m.place.pageNumber());
|
||||||
|
return cntr;
|
||||||
|
},
|
||||||
|
update: function (page, pageNumber) {
|
||||||
|
if (pageNumber) {
|
||||||
|
this.runners[page.m.pageIndex].innerHTML = pageNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.addControl(pageNumber, 'page');
|
||||||
|
reader.listen(
|
||||||
|
'monocle:pagechange',
|
||||||
|
function (evt) {
|
||||||
|
pageNumber.update(evt.m.page, evt.m.pageNumber);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Scrubber */
|
||||||
|
var scrubber = new Monocle.Controls.Scrubber(reader);
|
||||||
|
reader.addControl(scrubber, 'popover', { hidden: true });
|
||||||
|
var showFn = function (evt) {
|
||||||
|
evt.stopPropagation();
|
||||||
|
reader.showControl(scrubber);
|
||||||
|
scrubber.updateNeedles();
|
||||||
|
}
|
||||||
|
for (var i = 0; i < chapterTitle.runners.length; ++i) {
|
||||||
|
Monocle.Events.listenForContact(
|
||||||
|
chapterTitle.runners[i].parentNode,
|
||||||
|
{ start: showFn }
|
||||||
|
);
|
||||||
|
Monocle.Events.listenForContact(
|
||||||
|
pageNumber.runners[i].parentNode,
|
||||||
|
{ start: showFn }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})();
|
|
@ -12,6 +12,9 @@
|
||||||
{{? data.mail == 1}}
|
{{? data.mail == 1}}
|
||||||
<a id="mailButton" title="Mail" href="empty.php" onclick="sendToMailAddress (this, {{=data.id}}); return false;"><i class="icon-envelope icon-large"></i></a>
|
<a id="mailButton" title="Mail" href="empty.php" onclick="sendToMailAddress (this, {{=data.id}}); return false;"><i class="icon-envelope icon-large"></i></a>
|
||||||
{{?}}
|
{{?}}
|
||||||
|
{{? data.readerUrl != ""}}
|
||||||
|
<a title="Reader" href="{{=data.readerUrl}}" target="blank"><i class="icon-eye-open icon-large"></i></a>
|
||||||
|
{{?}}
|
||||||
<br />
|
<br />
|
||||||
{{~}}
|
{{~}}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
Loading…
Reference in a new issue