mirror of
https://github.com/futurepress/epub.js.git
synced 2025-10-03 14:59:18 +02:00
222 lines
4.3 KiB
JavaScript
222 lines
4.3 KiB
JavaScript
// Manage annotations for a book?
|
|
|
|
/*
|
|
let a = rendition.annotations.highlight(cfiRange, data)
|
|
|
|
a.on("added", () => console.log("added"))
|
|
a.on("removed", () => console.log("removed"))
|
|
a.on("clicked", () => console.log("clicked"))
|
|
|
|
a.update(data)
|
|
a.remove();
|
|
a.text();
|
|
|
|
rendition.annotations.show()
|
|
rendition.annotations.hide()
|
|
|
|
rendition.annotations.highlights.show()
|
|
rendition.annotations.highlights.hide()
|
|
*/
|
|
|
|
import EventEmitter from "event-emitter";
|
|
import EpubCFI from "./epubcfi";
|
|
|
|
/**
|
|
* Handles managing adding & removing Annotations
|
|
* @class
|
|
*/
|
|
class Annotations {
|
|
|
|
constructor (rendition) {
|
|
this.rendition = rendition;
|
|
this.highlights = [];
|
|
this.underlines = [];
|
|
this.marks = [];
|
|
this._annotations = {};
|
|
this._annotationsBySectionIndex = {};
|
|
|
|
this.rendition.hooks.render.register(this.inject.bind(this));
|
|
this.rendition.hooks.unloaded.register(this.clear.bind(this));
|
|
}
|
|
|
|
add (type, cfiRange, data, cb) {
|
|
let hash = encodeURI(cfiRange);
|
|
let cfi = new EpubCFI(cfiRange);
|
|
let sectionIndex = cfi.spinePos;
|
|
let annotation = new Annotation({
|
|
type,
|
|
cfiRange,
|
|
data,
|
|
sectionIndex,
|
|
cb
|
|
});
|
|
|
|
this._annotations[hash] = annotation;
|
|
|
|
if (sectionIndex in this._annotationsBySectionIndex) {
|
|
this._annotationsBySectionIndex[sectionIndex].push(hash);
|
|
} else {
|
|
this._annotationsBySectionIndex[sectionIndex] = [hash];
|
|
}
|
|
|
|
let views = this.rendition.views();
|
|
|
|
views.forEach( (view) => {
|
|
if (annotation.sectionIndex === view.index) {
|
|
annotation.attach(view);
|
|
}
|
|
});
|
|
|
|
return annotation;
|
|
}
|
|
|
|
remove (cfiRange, type) {
|
|
let hash = encodeURI(cfiRange);
|
|
|
|
if (hash in this._annotations) {
|
|
let annotation = this._annotations[hash];
|
|
|
|
if (type && annotation.type !== type) {
|
|
return;
|
|
}
|
|
|
|
let views = this.rendition.views();
|
|
views.forEach( (view) => {
|
|
this._removeFromAnnotationBySectionIndex(annotation.sectionIndex, hash);
|
|
if (annotation.sectionIndex === view.index) {
|
|
annotation.detach(view);
|
|
}
|
|
});
|
|
|
|
delete this._annotations[hash];
|
|
}
|
|
}
|
|
|
|
_removeFromAnnotationBySectionIndex (sectionIndex, hash) {
|
|
this._annotationsBySectionIndex[sectionIndex] = this._annotationsAt(sectionIndex).filter(h => h !== hash);
|
|
}
|
|
|
|
_annotationsAt (index) {
|
|
return this._annotationsBySectionIndex[index];
|
|
}
|
|
|
|
highlight (cfiRange, data, cb) {
|
|
this.add("highlight", cfiRange, data, cb);
|
|
}
|
|
|
|
underline (cfiRange, data, cb) {
|
|
this.add("underline", cfiRange, data, cb);
|
|
}
|
|
|
|
mark (cfiRange, data, cb) {
|
|
this.add("mark", cfiRange, data, cb);
|
|
}
|
|
|
|
each () {
|
|
return this._annotations.forEach.apply(this._annotations, arguments);
|
|
}
|
|
|
|
inject (view) {
|
|
let sectionIndex = view.index;
|
|
if (sectionIndex in this._annotationsBySectionIndex) {
|
|
let annotations = this._annotationsBySectionIndex[sectionIndex];
|
|
annotations.forEach((hash) => {
|
|
let annotation = this._annotations[hash];
|
|
annotation.attach(view);
|
|
});
|
|
}
|
|
}
|
|
|
|
clear (view) {
|
|
let sectionIndex = view.index;
|
|
if (sectionIndex in this._annotationsBySectionIndex) {
|
|
let annotations = this._annotationsBySectionIndex[sectionIndex];
|
|
annotations.forEach((hash) => {
|
|
let annotation = this._annotations[hash];
|
|
annotation.detach(view);
|
|
});
|
|
}
|
|
}
|
|
|
|
show () {
|
|
|
|
}
|
|
|
|
hide () {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class Annotation {
|
|
|
|
constructor ({
|
|
type,
|
|
cfiRange,
|
|
data,
|
|
sectionIndex,
|
|
cb
|
|
}) {
|
|
this.type = type;
|
|
this.cfiRange = cfiRange;
|
|
this.data = data;
|
|
this.sectionIndex = sectionIndex;
|
|
this.mark = undefined;
|
|
this.cb = cb;
|
|
}
|
|
|
|
update (data) {
|
|
this.data = data;
|
|
}
|
|
|
|
attach (view) {
|
|
let {cfiRange, data, type, mark, cb} = this;
|
|
let result;
|
|
/*
|
|
if (mark) {
|
|
return; // already added
|
|
}
|
|
*/
|
|
|
|
if (type === "highlight") {
|
|
result = view.highlight(cfiRange, data, cb);
|
|
} else if (type === "underline") {
|
|
result = view.underline(cfiRange, data, cb);
|
|
} else if (type === "mark") {
|
|
result = view.mark(cfiRange, data, cb);
|
|
}
|
|
|
|
this.mark = result;
|
|
|
|
return result;
|
|
}
|
|
|
|
detach (view) {
|
|
let {cfiRange, type} = this;
|
|
let result;
|
|
|
|
if (view) {
|
|
if (type === "highlight") {
|
|
result = view.unhighlight(cfiRange);
|
|
} else if (type === "underline") {
|
|
result = view.ununderline(cfiRange);
|
|
} else if (type === "mark") {
|
|
result = view.unmark(cfiRange);
|
|
}
|
|
}
|
|
|
|
this.mark = undefined;
|
|
|
|
return result;
|
|
}
|
|
|
|
text () {
|
|
// TODO: needs implementation in contents
|
|
}
|
|
|
|
}
|
|
|
|
EventEmitter(Annotation.prototype);
|
|
|
|
|
|
export default Annotations
|