Browse Source

Add monocle

--HG--
extra : rebase_source : 9503ba50aa
master
Sébastien Lucas 10 years ago
parent
commit
8a33a55fee
4 changed files with 6990 additions and 0 deletions
  1. +5641
    -0
      resources/monocle/scripts/monocore.js
  2. +985
    -0
      resources/monocle/scripts/monoctrl.js
  3. +195
    -0
      resources/monocle/styles/monocore.css
  4. +169
    -0
      resources/monocle/styles/monoctrl.css

+ 5641
- 0
resources/monocle/scripts/monocore.js
File diff suppressed because it is too large
View File


+ 985
- 0
resources/monocle/scripts/monoctrl.js View 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
- 0
resources/monocle/styles/monocore.css View 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
- 0
resources/monocle/styles/monoctrl.css View 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==);
}

Loading…
Cancel
Save