Skip to content

Commit

Permalink
Merge pull request #100 from d-i-t-a/develop
Browse files Browse the repository at this point in the history
V1.5.0 - Positions and Timeline Module
  • Loading branch information
aferditamuriqi authored Nov 24, 2020
2 parents ac694d1 + a42dfe2 commit 1c89202
Show file tree
Hide file tree
Showing 14 changed files with 1,253 additions and 145 deletions.
3 changes: 2 additions & 1 deletion examples/streamed/server/server-cli.inlinesourcemap.js
Original file line number Diff line number Diff line change
Expand Up @@ -1701,9 +1701,10 @@ function serverPub(server, topRouter) {
const urlBookShowAll = "./manifest.json/show/all";
const urlReaderAPI = "/viewer/index_api.html?url=PREFIX" + querystring.escape(urlBook);
const urlReaderMaterial = "/viewer/index_material.html?url=PREFIX" + querystring.escape(urlBook);
const urlD2E = "/viewer/index.html?url=PREFIX" + querystring.escape(urlBook);
const htmlLanding = "<html><body><h1>PATH_STR</h1><h2><a href='" +
urlBookShowAll + "'>" + urlBookShowAll + "</a></h2>" +
(server.disableReaders ? "" : ("<p>Reader API:<br><a href='" + urlReaderAPI + "'>" + urlReaderAPI + "</a></p><p>Reader Material:<br><a href='" + urlReaderMaterial + "'>" + urlReaderMaterial + "</a></p>")) +
(server.disableReaders ? "" : ("<p>Reader API:<br><a href='" + urlReaderAPI + "'>" + urlReaderAPI + "</a></p><p>Reader Material:<br><a href='" + urlReaderMaterial + "'>" + urlReaderMaterial + "</a></p><p>D2 Reader:<br><a href='" + urlD2E + "'>" + urlD2E + "</a></p>")) +
"</body></html>";
const routerPathBase64 = express.Router({ strict: false });
routerPathBase64.use(morgan("combined", { stream: { write: (msg) => debug(msg) } }));
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@d-i-t-a/reader",
"version": "1.4.0",
"version": "1.5.0",
"description": "A viewer application for EPUB files.",
"repository": "https://github.com/d-i-t-a/R2D2BC",
"license": "Apache-2.0",
Expand Down
84 changes: 84 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { oc } from "ts-optchain"
import { TTSSettings } from "./modules/TTS/TTSSettings";
import SearchModule from "./modules/search/SearchModule";
import TextHighlighter from "./modules/highlight/TextHighlighter";
import { Locator } from "./model/Locator";
import TimelineModule from "./modules/positions/TimelineModule";

var R2Settings: UserSettings;
var R2TTSSettings: TTSSettings;
Expand All @@ -38,6 +40,7 @@ var BookmarkModuleInstance: BookmarkModule;
var AnnotationModuleInstance: AnnotationModule;
var TTSModuleInstance: TTSModule;
var SearchModuleInstance:SearchModule;
var TimelineModuleInstance: TimelineModule;

export const IS_DEV = (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "dev");

Expand All @@ -60,6 +63,9 @@ export async function unload() {
if (oc(R2Navigator.rights).enableSearch(false)) {
SearchModuleInstance.stop()
}
if (oc(R2Navigator.rights).enableTimeline(false)) {
TimelineModuleInstance.stop()
}
}
export function startReadAloud() {
if (IS_DEV) { console.log("startReadAloud") }
Expand Down Expand Up @@ -251,6 +257,18 @@ export async function scroll(value) {
R2Settings.scroll(value)
}

export async function currentLocator() {
if (IS_DEV) { console.log("currentLocator") }
return R2Navigator.currentLocator()
}
export async function positions() {
if (IS_DEV) { console.log("positions") }
return R2Navigator.positions()
}
export async function goToPosition(value) {
if (IS_DEV) { console.log("goToPosition") }
return R2Navigator.goToPosition(value)
}

export async function load(config: ReaderConfig): Promise<any> {
var mainElement = document.getElementById("D2Reader-Container");
Expand All @@ -275,6 +293,53 @@ export async function load(config: ReaderConfig): Promise<any> {

const publication: Publication = await Publication.getManifest(webpubManifestUrl, store);

var startPosition = 0
var totalContentLength = 0
var positions = []
publication.readingOrder.map(async (link, index) => {
var href = publication.getAbsoluteHref(link.href);
await fetch(href)
.then(async r => {
let length = (await r.blob()).size
link.contentLength = length
totalContentLength += length
let positionLength = 1024
let positionCount = Math.max(1, Math.ceil(length / positionLength))
if (IS_DEV) console.log(length + " Bytes")
if (IS_DEV) console.log(positionCount + " Positions")
Array.from(Array(positionCount).keys()).map((_, position) => {
const locator: Locator = {
href: link.href,
locations: {
progression: (position) / (positionCount),
position: startPosition + (position + 1),
},
type: link.type
};
if (IS_DEV) console.log(locator)
positions.push(locator)
});
startPosition = startPosition + positionCount
})
if (index + 1 == publication.readingOrder.length) {
publication.readingOrder.map(async (link) => {
if (IS_DEV) console.log(totalContentLength)
if (IS_DEV) console.log(link.contentLength)
link.contentWeight = 100 / totalContentLength * link.contentLength
if (IS_DEV) console.log(link.contentWeight)
})
positions.map((locator, _index) => {
let resource = positions.filter((el: Locator) => el.href === locator.href)
let positionIndex = Math.ceil(locator.locations.progression * (resource.length - 1))
locator.locations.totalProgression = (locator.locations.position - 1) / (positions.length)
locator.locations.remainingPositions = Math.abs((positionIndex) - (resource.length - 1))
locator.locations.totalRemainingPositions = Math.abs((locator.locations.position - 1) - (positions.length - 1))
})
publication.positions = positions
if (IS_DEV) console.log(positions)
}
});

// Settings
R2Settings = await UserSettings.create({
store: settingsStore,
Expand Down Expand Up @@ -364,6 +429,16 @@ export async function load(config: ReaderConfig): Promise<any> {
});
}

// Timeline Module
if (oc(config.rights).enableTimeline(false)) {
TimelineModule.create({
publication: publication,
delegate: R2Navigator
}).then(function (timelineModule) {
TimelineModuleInstance = timelineModule
})
}

return new Promise(resolve => resolve(R2Navigator));
}

Expand Down Expand Up @@ -498,4 +573,13 @@ exports.goToSearchID = function (href, index, current) {
}
exports.clearSearch = function () {
clearSearch()
}
exports.currentLocator = function () {
return currentLocator()
}
exports.positions = function () {
return positions()
}
exports.goToPosition = function (value) {
goToPosition(value)
}
5 changes: 4 additions & 1 deletion src/model/Locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ import { IHighlight } from "../modules/highlight/common/highlight";
export interface Locator {
href: string;
type?: string;
title: string;
title?: string;
locations: Locations;
text?: LocatorText;
displayInfo?: any;
}


Expand All @@ -39,6 +40,8 @@ export interface Locations {
progression?: number; // 3 = bookmarks
position?: number; // 4 = goto page
totalProgression?: number;
remainingPositions?: number;
totalRemainingPositions?: number;
}

export interface ReadingPosition extends Locator {
Expand Down
18 changes: 17 additions & 1 deletion src/model/Publication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

import Store from "../store/Store";
import { Locator } from "./Locator";

export interface Metadata {
title?: string;
Expand Down Expand Up @@ -54,6 +55,8 @@ export interface Link {
// The MediaOverlays associated to the resource of the Link
// mediaOverlays?: MediaOverlays;

contentLength?: number;
contentWeight?: number;
}

export default class Publication {
Expand All @@ -65,6 +68,7 @@ export default class Publication {
public readonly landmarks: Array<Link>;
public readonly pageList: Array<Link>;
public readonly images: Array<Link>;
public positions: Array<Locator>;

private readonly manifestUrl: URL;

Expand Down Expand Up @@ -158,7 +162,11 @@ export default class Publication {
}
public getRelativeHref(href: string): string | null {
const manifest = this.manifestUrl.href.replace("/manifest.json", ""); //new URL(this.manifestUrl.href, this.manifestUrl.href).href;
return href.replace(manifest, "");
var href = href.replace(manifest, "");
if(href.charAt(0) === '/') {
href = href.substring(1);
}
return href;
}


Expand Down Expand Up @@ -215,4 +223,12 @@ export default class Publication {
}
return link
}

/**
* positionsByHref
*/
public positionsByHref(href:string) {
return this.positions.filter((el: Locator) => el.href === href)
}

}
141 changes: 141 additions & 0 deletions src/modules/positions/TimelineModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright 2018-2020 DITA (AM Consulting LLC)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Developed on behalf of: DITA
* Licensed to: Bibliotheca LLC, Bokbasen AS and CAST under one or more contributor license agreements.
*/

import { IS_DEV } from "../..";
import { Locator } from "../../model/Locator";
import Publication from "../../model/Publication";
import IFrameNavigator from "../../navigator/IFrameNavigator";
import { addEventListenerOptional } from "../../utils/EventHandler";
import ReaderModule from "../ReaderModule";
import * as HTMLUtilities from "../../utils/HTMLUtilities";
import { oc } from "ts-optchain";

export interface TimelineModuleConfig {
publication: Publication;
delegate: IFrameNavigator;
}

export default class TimelineModule implements ReaderModule {

private publication: Publication;
private delegate: IFrameNavigator;
private timelineContainer: HTMLDivElement;
private positionSlider: HTMLInputElement;

public static async create(config: TimelineModuleConfig) {
const timeline = new this(
config.delegate,
config.publication
);

await timeline.start();
return timeline;
}

private constructor(delegate: IFrameNavigator, publication: Publication) {
this.delegate = delegate
this.publication = publication
}

async stop() {
if (IS_DEV) { console.log("Timeline module stop") }
}

protected async start(): Promise<void> {

this.delegate.timelineModule = this

this.timelineContainer = HTMLUtilities.findElement(document, "#container-view-timeline") as HTMLDivElement;
if (oc(this.delegate.rights).enableMaterial(false)) {
this.positionSlider = HTMLUtilities.findElement(document, "#positionSlider") as HTMLInputElement;
}
}

async initialize() {
return new Promise(async (resolve) => {
await (document as any).fonts.ready;

let locator = this.delegate.currentLocator()
if (oc(this.delegate.rights).enableMaterial(false)) {
this.positionSlider.value = locator.locations.position.toString()
this.positionSlider.max = (locator.locations.totalRemainingPositions + locator.locations.position).toString()
}

this.timelineContainer.innerHTML = ""
this.publication.readingOrder.forEach(link => {

console.log(link.contentWeight)
const linkHref = this.publication.getAbsoluteHref(link.href);
const tocItemAbs = this.publication.getTOCItemAbsolute(linkHref);
const tocHref = (tocItemAbs.href.indexOf("#") !== -1) ? tocItemAbs.href.slice(0, tocItemAbs.href.indexOf("#")) : tocItemAbs.href
const tocHrefAbs = this.publication.getAbsoluteHref(tocHref);

var chapterHeight
if (this.publication.positions) {
if (link.contentWeight) {
chapterHeight = link.contentWeight
} else {
chapterHeight = 5
}
} else {
chapterHeight = 100 / this.publication.readingOrder.length
}

var chapter = document.createElement("div")
chapter.style.height = chapterHeight + "%"
chapter.style.width = "100%"
chapter.className = "chapter";

if (tocItemAbs.title !== undefined) {
var tooltip = document.createElement("span")
tooltip.innerHTML = tocItemAbs.title;
tooltip.className = "chapter-tooltip";
chapter.appendChild(tooltip);
}

addEventListenerOptional(chapter, 'click', (event: MouseEvent) => {

event.preventDefault();
event.stopPropagation();

const position1 = this.publication.positions.filter((el: Locator) => el.href === link.href)[0]
position1.href = this.publication.getAbsoluteHref(position1.href)
console.log(position1)
this.delegate.navigate(position1)
});

if (tocHrefAbs === this.delegate.currentChapterLink.href) {
chapter.className += " active";
} else {
chapter.className = chapter.className.replace(" active", "");
}

// append bookmarks indicator
// append notes indicator
// append highlights indicator

this.timelineContainer.appendChild(chapter)

});

resolve()
});
}

}
Loading

0 comments on commit 1c89202

Please sign in to comment.