diff --git a/examples/highlights.html b/examples/highlights.html index 3f8bd9b..0596cbd 100644 --- a/examples/highlights.html +++ b/examples/highlights.html @@ -9,77 +9,9 @@ - - +
-
- - +
+ +
- + + + + + + + +
+
+ + + + + + + diff --git a/karma.conf.js b/karma.conf.js index d998a00..c96bb62 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -58,7 +58,7 @@ module.exports = function(config) { loaders: [ { test: /\.js$/, - exclude: /node_modules/, + exclude: /node_modules\/(?!(marks)\/).*/, loader: "babel-loader", query: { presets: ['es2015'], diff --git a/package.json b/package.json index 3efd665..61df609 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,6 @@ "version": "0.3.25", "description": "Parse and Render Epubs", "main": "lib/index.js", - "jsnext:main": "src/index.js", "module": "src/index.js", "repository": "https://github.com/futurepress/epub.js", "directories": { @@ -73,6 +72,7 @@ "dependencies": { "event-emitter": "^0.3.4", "jszip": "^3.1.3", + "marks": "github:fchasen/marks", "path-webpack": "^0.0.3", "stream-browserify": "^2.0.1", "xmldom": "^0.1.27" diff --git a/src/contents.js b/src/contents.js index 818f356..72ff369 100644 --- a/src/contents.js +++ b/src/contents.js @@ -3,6 +3,7 @@ import {isNumber, prefixed} from "./utils/core"; import EpubCFI from "./epubcfi"; import Mapping from "./mapping"; import {replaceLinks} from "./utils/replacements"; +import { Pane, Highlight, Underline } from "marks"; // Dom events to listen for const EVENTS = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"]; @@ -24,6 +25,8 @@ class Contents { this.cfiBase = cfiBase || ""; + this.pane = undefined; + this.listeners(); } @@ -307,6 +310,8 @@ class Contents { height: height }; + this.pane && this.pane.render(); + this.emit("resize", this._size); } @@ -464,7 +469,8 @@ class Contents { if ( !ready && (!this.readyState || this.readyState == "complete") ) { ready = true; // Let apply - setTimeout(function(){ + setTimeout(() => { + this.pane && this.pane.render(); resolve(true); }, 1); } @@ -525,6 +531,7 @@ class Contents { styleSheet.insertRule(`${selector}{${result}}`, styleSheet.cssRules.length); }); } + this.pane && this.pane.render(); } addScript(src) { @@ -625,7 +632,7 @@ class Contents { this.selectionEndTimeout = setTimeout(function() { var selection = this.window.getSelection(); this.triggerSelectedEvent(selection); - }.bind(this), 500); + }.bind(this), 250); } triggerSelectedEvent(selection){ @@ -747,6 +754,63 @@ class Contents { }); } + highlight(cfiRange, data={}, cb) { + let range = this.range(cfiRange); + + data["epubcfi"] = cfiRange; + + if (!this.pane) { + this.pane = new Pane(this.content, this.document.body); + } + + let m = new Highlight(range, "epubjs-hl", data, {'fill': 'yellow', 'fill-opacity': '0.3', 'mix-blend-mode': 'multiply'}); + let h = this.pane.addMark(m); + if (cb) { + h.element.addEventListener("click", cb); + } + return h; + } + + underline(cfiRange, data={}, cb) { + let range = this.range(cfiRange); + + data["epubcfi"] = cfiRange; + + if (!this.pane) { + this.pane = new Pane(this.content, this.document.body); + } + + let m = new Underline(range, "epubjs-ul", data, {'stroke': 'black', 'stroke-opacity': '0.3', 'mix-blend-mode': 'multiply'}); + let h = this.pane.addMark(m); + if (cb) { + h.element.addEventListener("click", cb); + } + return h; + } + + mark(cfiRange, data={}, cb) { + let range = this.range(cfiRange); + + let container = range.commonAncestorContainer; + let parent = (container.nodeType === 1) ? container : container.parentNode; + + parent.setAttribute("ref", "epubjs-mk"); + + parent.dataset["epubcfi"] = cfiRange; + + if (data) { + Object.entries(data).forEach(([key, val]) => { + parent.dataset[key] = val; + }); + } + + + if (cb) { + parent.addEventListener("click", cb); + } + + } + destroy() { // Stop observing if(this.observer) { diff --git a/src/rendition.js b/src/rendition.js index 6c79e08..a3c97a0 100644 --- a/src/rendition.js +++ b/src/rendition.js @@ -310,7 +310,7 @@ class Rendition { */ afterDisplayed(view){ this.hooks.content.trigger(view.contents, this); - this.emit("rendered", view.section); + this.emit("rendered", view.section, view); // this.reportLocation(); } @@ -550,11 +550,11 @@ class Rendition { passEvents(contents){ var listenedEvents = Contents.listenedEvents; - listenedEvents.forEach(function(e){ - contents.on(e, this.triggerViewEvent.bind(this)); - }.bind(this)); + listenedEvents.forEach((e) => { + contents.on(e, (ev) => this.triggerViewEvent(ev, contents)); + }); - contents.on("selected", this.triggerSelectedEvent.bind(this)); + contents.on("selected", (e) => this.triggerSelectedEvent(e, contents)); } /** @@ -562,8 +562,8 @@ class Rendition { * @private * @param {event} e */ - triggerViewEvent(e){ - this.emit(e.type, e); + triggerViewEvent(e, contents){ + this.emit(e.type, e, contents); } /** @@ -571,8 +571,8 @@ class Rendition { * @private * @param {EpubCFI} cfirange */ - triggerSelectedEvent(cfirange){ - this.emit("selected", cfirange); + triggerSelectedEvent(cfirange, contents){ + this.emit("selected", cfirange, contents); } /** diff --git a/webpack.config.js b/webpack.config.js index 35259bb..24d1238 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -44,7 +44,7 @@ module.exports = { loaders: [ { test: /\.js$/, - exclude: /node_modules/, + exclude: /node_modules\/(?!(marks)\/).*/, loader: "babel-loader", query: LEGACY ? { presets: ['es2015'],