From 4fd9b8f2b5a98bfcde57970b48fed2488a80f356 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 17 Sep 2021 21:53:37 +0300 Subject: add in master snapshot of epubjs --- lib/epub.js/src/managers/views/iframe.js | 835 +++++++++++++++++++++++++++++++ lib/epub.js/src/managers/views/inline.js | 432 ++++++++++++++++ 2 files changed, 1267 insertions(+) create mode 100644 lib/epub.js/src/managers/views/iframe.js create mode 100644 lib/epub.js/src/managers/views/inline.js (limited to 'lib/epub.js/src/managers/views') diff --git a/lib/epub.js/src/managers/views/iframe.js b/lib/epub.js/src/managers/views/iframe.js new file mode 100644 index 0000000..76b2c1d --- /dev/null +++ b/lib/epub.js/src/managers/views/iframe.js @@ -0,0 +1,835 @@ +import EventEmitter from "event-emitter"; +import {extend, borders, uuid, isNumber, bounds, defer, createBlobUrl, revokeBlobUrl} from "../../utils/core"; +import EpubCFI from "../../epubcfi"; +import Contents from "../../contents"; +import { EVENTS } from "../../utils/constants"; +import { Pane, Highlight, Underline } from "marks-pane"; + +class IframeView { + constructor(section, options) { + this.settings = extend({ + ignoreClass : "", + axis: undefined, //options.layout && options.layout.props.flow === "scrolled" ? "vertical" : "horizontal", + direction: undefined, + width: 0, + height: 0, + layout: undefined, + globalLayoutProperties: {}, + method: undefined, + forceRight: false + }, options || {}); + + this.id = "epubjs-view-" + uuid(); + this.section = section; + this.index = section.index; + + this.element = this.container(this.settings.axis); + + this.added = false; + this.displayed = false; + this.rendered = false; + + // this.width = this.settings.width; + // this.height = this.settings.height; + + this.fixedWidth = 0; + this.fixedHeight = 0; + + // Blank Cfi for Parsing + this.epubcfi = new EpubCFI(); + + this.layout = this.settings.layout; + // Dom events to listen for + // this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"]; + + this.pane = undefined; + this.highlights = {}; + this.underlines = {}; + this.marks = {}; + + } + + container(axis) { + var element = document.createElement("div"); + + element.classList.add("epub-view"); + + // this.element.style.minHeight = "100px"; + element.style.height = "0px"; + element.style.width = "0px"; + element.style.overflow = "hidden"; + element.style.position = "relative"; + element.style.display = "block"; + + if(axis && axis == "horizontal"){ + element.style.flex = "none"; + } else { + element.style.flex = "initial"; + } + + return element; + } + + create() { + + if(this.iframe) { + return this.iframe; + } + + if(!this.element) { + this.element = this.createContainer(); + } + + this.iframe = document.createElement("iframe"); + this.iframe.id = this.id; + this.iframe.scrolling = "no"; // Might need to be removed: breaks ios width calculations + this.iframe.style.overflow = "hidden"; + this.iframe.seamless = "seamless"; + // Back up if seamless isn't supported + this.iframe.style.border = "none"; + + this.iframe.setAttribute("enable-annotation", "true"); + + this.resizing = true; + + // this.iframe.style.display = "none"; + this.element.style.visibility = "hidden"; + this.iframe.style.visibility = "hidden"; + + this.iframe.style.width = "0"; + this.iframe.style.height = "0"; + this._width = 0; + this._height = 0; + + this.element.setAttribute("ref", this.index); + + this.added = true; + + this.elementBounds = bounds(this.element); + + // if(width || height){ + // this.resize(width, height); + // } else if(this.width && this.height){ + // this.resize(this.width, this.height); + // } else { + // this.iframeBounds = bounds(this.iframe); + // } + + + if(("srcdoc" in this.iframe)) { + this.supportsSrcdoc = true; + } else { + this.supportsSrcdoc = false; + } + + if (!this.settings.method) { + this.settings.method = this.supportsSrcdoc ? "srcdoc" : "write"; + } + + return this.iframe; + } + + render(request, show) { + + // view.onLayout = this.layout.format.bind(this.layout); + this.create(); + + // Fit to size of the container, apply padding + this.size(); + + if(!this.sectionRender) { + this.sectionRender = this.section.render(request); + } + + // Render Chain + return this.sectionRender + .then(function(contents){ + return this.load(contents); + }.bind(this)) + .then(function(){ + + // find and report the writingMode axis + let writingMode = this.contents.writingMode(); + + // Set the axis based on the flow and writing mode + let axis; + if (this.settings.flow === "scrolled") { + axis = (writingMode.indexOf("vertical") === 0) ? "horizontal" : "vertical"; + } else { + axis = (writingMode.indexOf("vertical") === 0) ? "vertical" : "horizontal"; + } + + if (writingMode.indexOf("vertical") === 0 && this.settings.flow === "paginated") { + this.layout.delta = this.layout.height; + } + + this.setAxis(axis); + this.emit(EVENTS.VIEWS.AXIS, axis); + + this.setWritingMode(writingMode); + this.emit(EVENTS.VIEWS.WRITING_MODE, writingMode); + + + // apply the layout function to the contents + this.layout.format(this.contents, this.section, this.axis); + + // Listen for events that require an expansion of the iframe + this.addListeners(); + + return new Promise((resolve, reject) => { + // Expand the iframe to the full size of the content + this.expand(); + + if (this.settings.forceRight) { + this.element.style.marginLeft = this.width() + "px"; + } + resolve(); + }); + + }.bind(this), function(e){ + this.emit(EVENTS.VIEWS.LOAD_ERROR, e); + return new Promise((resolve, reject) => { + reject(e); + }); + }.bind(this)) + .then(function() { + this.emit(EVENTS.VIEWS.RENDERED, this.section); + }.bind(this)); + + } + + reset () { + if (this.iframe) { + this.iframe.style.width = "0"; + this.iframe.style.height = "0"; + this._width = 0; + this._height = 0; + this._textWidth = undefined; + this._contentWidth = undefined; + this._textHeight = undefined; + this._contentHeight = undefined; + } + this._needsReframe = true; + } + + // Determine locks base on settings + size(_width, _height) { + var width = _width || this.settings.width; + var height = _height || this.settings.height; + + if(this.layout.name === "pre-paginated") { + this.lock("both", width, height); + } else if(this.settings.axis === "horizontal") { + this.lock("height", width, height); + } else { + this.lock("width", width, height); + } + + this.settings.width = width; + this.settings.height = height; + } + + // Lock an axis to element dimensions, taking borders into account + lock(what, width, height) { + var elBorders = borders(this.element); + var iframeBorders; + + if(this.iframe) { + iframeBorders = borders(this.iframe); + } else { + iframeBorders = {width: 0, height: 0}; + } + + if(what == "width" && isNumber(width)){ + this.lockedWidth = width - elBorders.width - iframeBorders.width; + // this.resize(this.lockedWidth, width); // width keeps ratio correct + } + + if(what == "height" && isNumber(height)){ + this.lockedHeight = height - elBorders.height - iframeBorders.height; + // this.resize(width, this.lockedHeight); + } + + if(what === "both" && + isNumber(width) && + isNumber(height)){ + + this.lockedWidth = width - elBorders.width - iframeBorders.width; + this.lockedHeight = height - elBorders.height - iframeBorders.height; + // this.resize(this.lockedWidth, this.lockedHeight); + } + + if(this.displayed && this.iframe) { + + // this.contents.layout(); + this.expand(); + } + + + + } + + // Resize a single axis based on content dimensions + expand(force) { + var width = this.lockedWidth; + var height = this.lockedHeight; + var columns; + + var textWidth, textHeight; + + if(!this.iframe || this._expanding) return; + + this._expanding = true; + + if(this.layout.name === "pre-paginated") { + width = this.layout.columnWidth; + height = this.layout.height; + } + // Expand Horizontally + else if(this.settings.axis === "horizontal") { + // Get the width of the text + width = this.contents.textWidth(); + + if (width % this.layout.pageWidth > 0) { + width = Math.ceil(width / this.layout.pageWidth) * this.layout.pageWidth; + } + + if (this.settings.forceEvenPages) { + columns = (width / this.layout.pageWidth); + if ( this.layout.divisor > 1 && + this.layout.name === "reflowable" && + (columns % 2 > 0)) { + // add a blank page + width += this.layout.pageWidth; + } + } + + } // Expand Vertically + else if(this.settings.axis === "vertical") { + height = this.contents.textHeight(); + if (this.settings.flow === "paginated" && + height % this.layout.height > 0) { + height = Math.ceil(height / this.layout.height) * this.layout.height; + } + } + + // Only Resize if dimensions have changed or + // if Frame is still hidden, so needs reframing + if(this._needsReframe || width != this._width || height != this._height){ + this.reframe(width, height); + } + + this._expanding = false; + } + + reframe(width, height) { + var size; + + if(isNumber(width)){ + this.element.style.width = width + "px"; + this.iframe.style.width = width + "px"; + this._width = width; + } + + if(isNumber(height)){ + this.element.style.height = height + "px"; + this.iframe.style.height = height + "px"; + this._height = height; + } + + let widthDelta = this.prevBounds ? width - this.prevBounds.width : width; + let heightDelta = this.prevBounds ? height - this.prevBounds.height : height; + + size = { + width: width, + height: height, + widthDelta: widthDelta, + heightDelta: heightDelta, + }; + + this.pane && this.pane.render(); + + requestAnimationFrame(() => { + let mark; + for (let m in this.marks) { + if (this.marks.hasOwnProperty(m)) { + mark = this.marks[m]; + this.placeMark(mark.element, mark.range); + } + } + }); + + this.onResize(this, size); + + this.emit(EVENTS.VIEWS.RESIZED, size); + + this.prevBounds = size; + + this.elementBounds = bounds(this.element); + + } + + + load(contents) { + var loading = new defer(); + var loaded = loading.promise; + + if(!this.iframe) { + loading.reject(new Error("No Iframe Available")); + return loaded; + } + + this.iframe.onload = function(event) { + + this.onLoad(event, loading); + + }.bind(this); + + if (this.settings.method === "blobUrl") { + this.blobUrl = createBlobUrl(contents, "application/xhtml+xml"); + this.iframe.src = this.blobUrl; + this.element.appendChild(this.iframe); + } else if(this.settings.method === "srcdoc"){ + this.iframe.srcdoc = contents; + this.element.appendChild(this.iframe); + } else { + + this.element.appendChild(this.iframe); + + this.document = this.iframe.contentDocument; + + if(!this.document) { + loading.reject(new Error("No Document Available")); + return loaded; + } + + this.iframe.contentDocument.open(); + // For Cordova windows platform + if(window.MSApp && MSApp.execUnsafeLocalFunction) { + var outerThis = this; + MSApp.execUnsafeLocalFunction(function () { + outerThis.iframe.contentDocument.write(contents); + }); + } else { + this.iframe.contentDocument.write(contents); + } + this.iframe.contentDocument.close(); + + } + + return loaded; + } + + onLoad(event, promise) { + + this.window = this.iframe.contentWindow; + this.document = this.iframe.contentDocument; + + this.contents = new Contents(this.document, this.document.body, this.section.cfiBase, this.section.index); + + this.rendering = false; + + var link = this.document.querySelector("link[rel='canonical']"); + if (link) { + link.setAttribute("href", this.section.canonical); + } else { + link = this.document.createElement("link"); + link.setAttribute("rel", "canonical"); + link.setAttribute("href", this.section.canonical); + this.document.querySelector("head").appendChild(link); + } + + this.contents.on(EVENTS.CONTENTS.EXPAND, () => { + if(this.displayed && this.iframe) { + this.expand(); + if (this.contents) { + this.layout.format(this.contents); + } + } + }); + + this.contents.on(EVENTS.CONTENTS.RESIZE, (e) => { + if(this.displayed && this.iframe) { + this.expand(); + if (this.contents) { + this.layout.format(this.contents); + } + } + }); + + promise.resolve(this.contents); + } + + setLayout(layout) { + this.layout = layout; + + if (this.contents) { + this.layout.format(this.contents); + this.expand(); + } + } + + setAxis(axis) { + + this.settings.axis = axis; + + if(axis == "horizontal"){ + this.element.style.flex = "none"; + } else { + this.element.style.flex = "initial"; + } + + this.size(); + + } + + setWritingMode(mode) { + // this.element.style.writingMode = writingMode; + this.writingMode = mode; + } + + addListeners() { + //TODO: Add content listeners for expanding + } + + removeListeners(layoutFunc) { + //TODO: remove content listeners for expanding + } + + display(request) { + var displayed = new defer(); + + if (!this.displayed) { + + this.render(request) + .then(function () { + + this.emit(EVENTS.VIEWS.DISPLAYED, this); + this.onDisplayed(this); + + this.displayed = true; + displayed.resolve(this); + + }.bind(this), function (err) { + displayed.reject(err, this); + }); + + } else { + displayed.resolve(this); + } + + + return displayed.promise; + } + + show() { + + this.element.style.visibility = "visible"; + + if(this.iframe){ + this.iframe.style.visibility = "visible"; + + // Remind Safari to redraw the iframe + this.iframe.style.transform = "translateZ(0)"; + this.iframe.offsetWidth; + this.iframe.style.transform = null; + } + + this.emit(EVENTS.VIEWS.SHOWN, this); + } + + hide() { + // this.iframe.style.display = "none"; + this.element.style.visibility = "hidden"; + this.iframe.style.visibility = "hidden"; + + this.stopExpanding = true; + this.emit(EVENTS.VIEWS.HIDDEN, this); + } + + offset() { + return { + top: this.element.offsetTop, + left: this.element.offsetLeft + } + } + + width() { + return this._width; + } + + height() { + return this._height; + } + + position() { + return this.element.getBoundingClientRect(); + } + + locationOf(target) { + var parentPos = this.iframe.getBoundingClientRect(); + var targetPos = this.contents.locationOf(target, this.settings.ignoreClass); + + return { + "left": targetPos.left, + "top": targetPos.top + }; + } + + onDisplayed(view) { + // Stub, override with a custom functions + } + + onResize(view, e) { + // Stub, override with a custom functions + } + + bounds(force) { + if(force || !this.elementBounds) { + this.elementBounds = bounds(this.element); + } + + return this.elementBounds; + } + + highlight(cfiRange, data={}, cb, className = "epubjs-hl", styles = {}) { + if (!this.contents) { + return; + } + const attributes = Object.assign({"fill": "yellow", "fill-opacity": "0.3", "mix-blend-mode": "multiply"}, styles); + let range = this.contents.range(cfiRange); + + let emitter = () => { + this.emit(EVENTS.VIEWS.MARK_CLICKED, cfiRange, data); + }; + + data["epubcfi"] = cfiRange; + + if (!this.pane) { + this.pane = new Pane(this.iframe, this.element); + } + + let m = new Highlight(range, className, data, attributes); + let h = this.pane.addMark(m); + + this.highlights[cfiRange] = { "mark": h, "element": h.element, "listeners": [emitter, cb] }; + + h.element.setAttribute("ref", className); + h.element.addEventListener("click", emitter); + h.element.addEventListener("touchstart", emitter); + + if (cb) { + h.element.addEventListener("click", cb); + h.element.addEventListener("touchstart", cb); + } + return h; + } + + underline(cfiRange, data={}, cb, className = "epubjs-ul", styles = {}) { + if (!this.contents) { + return; + } + const attributes = Object.assign({"stroke": "black", "stroke-opacity": "0.3", "mix-blend-mode": "multiply"}, styles); + let range = this.contents.range(cfiRange); + let emitter = () => { + this.emit(EVENTS.VIEWS.MARK_CLICKED, cfiRange, data); + }; + + data["epubcfi"] = cfiRange; + + if (!this.pane) { + this.pane = new Pane(this.iframe, this.element); + } + + let m = new Underline(range, className, data, attributes); + let h = this.pane.addMark(m); + + this.underlines[cfiRange] = { "mark": h, "element": h.element, "listeners": [emitter, cb] }; + + h.element.setAttribute("ref", className); + h.element.addEventListener("click", emitter); + h.element.addEventListener("touchstart", emitter); + + if (cb) { + h.element.addEventListener("click", cb); + h.element.addEventListener("touchstart", cb); + } + return h; + } + + mark(cfiRange, data={}, cb) { + if (!this.contents) { + return; + } + + if (cfiRange in this.marks) { + let item = this.marks[cfiRange]; + return item; + } + + let range = this.contents.range(cfiRange); + if (!range) { + return; + } + let container = range.commonAncestorContainer; + let parent = (container.nodeType === 1) ? container : container.parentNode; + + let emitter = (e) => { + this.emit(EVENTS.VIEWS.MARK_CLICKED, cfiRange, data); + }; + + if (range.collapsed && container.nodeType === 1) { + range = new Range(); + range.selectNodeContents(container); + } else if (range.collapsed) { // Webkit doesn't like collapsed ranges + range = new Range(); + range.selectNodeContents(parent); + } + + let mark = this.document.createElement("a"); + mark.setAttribute("ref", "epubjs-mk"); + mark.style.position = "absolute"; + + mark.dataset["epubcfi"] = cfiRange; + + if (data) { + Object.keys(data).forEach((key) => { + mark.dataset[key] = data[key]; + }); + } + + if (cb) { + mark.addEventListener("click", cb); + mark.addEventListener("touchstart", cb); + } + + mark.addEventListener("click", emitter); + mark.addEventListener("touchstart", emitter); + + this.placeMark(mark, range); + + this.element.appendChild(mark); + + this.marks[cfiRange] = { "element": mark, "range": range, "listeners": [emitter, cb] }; + + return parent; + } + + placeMark(element, range) { + let top, right, left; + + if(this.layout.name === "pre-paginated" || + this.settings.axis !== "horizontal") { + let pos = range.getBoundingClientRect(); + top = pos.top; + right = pos.right; + } else { + // Element might break columns, so find the left most element + let rects = range.getClientRects(); + + let rect; + for (var i = 0; i != rects.length; i++) { + rect = rects[i]; + if (!left || rect.left < left) { + left = rect.left; + // right = rect.right; + right = Math.ceil(left / this.layout.props.pageWidth) * this.layout.props.pageWidth - (this.layout.gap / 2); + top = rect.top; + } + } + } + + element.style.top = `${top}px`; + element.style.left = `${right}px`; + } + + unhighlight(cfiRange) { + let item; + if (cfiRange in this.highlights) { + item = this.highlights[cfiRange]; + + this.pane.removeMark(item.mark); + item.listeners.forEach((l) => { + if (l) { + item.element.removeEventListener("click", l); + item.element.removeEventListener("touchstart", l); + }; + }); + delete this.highlights[cfiRange]; + } + } + + ununderline(cfiRange) { + let item; + if (cfiRange in this.underlines) { + item = this.underlines[cfiRange]; + this.pane.removeMark(item.mark); + item.listeners.forEach((l) => { + if (l) { + item.element.removeEventListener("click", l); + item.element.removeEventListener("touchstart", l); + }; + }); + delete this.underlines[cfiRange]; + } + } + + unmark(cfiRange) { + let item; + if (cfiRange in this.marks) { + item = this.marks[cfiRange]; + this.element.removeChild(item.element); + item.listeners.forEach((l) => { + if (l) { + item.element.removeEventListener("click", l); + item.element.removeEventListener("touchstart", l); + }; + }); + delete this.marks[cfiRange]; + } + } + + destroy() { + + for (let cfiRange in this.highlights) { + this.unhighlight(cfiRange); + } + + for (let cfiRange in this.underlines) { + this.ununderline(cfiRange); + } + + for (let cfiRange in this.marks) { + this.unmark(cfiRange); + } + + if (this.blobUrl) { + revokeBlobUrl(this.blobUrl); + } + + if(this.displayed){ + this.displayed = false; + + this.removeListeners(); + this.contents.destroy(); + + this.stopExpanding = true; + this.element.removeChild(this.iframe); + + this.iframe = undefined; + this.contents = undefined; + + this._textWidth = null; + this._textHeight = null; + this._width = null; + this._height = null; + } + + // this.element.style.height = "0px"; + // this.element.style.width = "0px"; + } +} + +EventEmitter(IframeView.prototype); + +export default IframeView; diff --git a/lib/epub.js/src/managers/views/inline.js b/lib/epub.js/src/managers/views/inline.js new file mode 100644 index 0000000..072b586 --- /dev/null +++ b/lib/epub.js/src/managers/views/inline.js @@ -0,0 +1,432 @@ +import EventEmitter from "event-emitter"; +import {extend, borders, uuid, isNumber, bounds, defer, qs, parse} from "../../utils/core"; +import EpubCFI from "../../epubcfi"; +import Contents from "../../contents"; +import { EVENTS } from "../../utils/constants"; + +class InlineView { + constructor(section, options) { + this.settings = extend({ + ignoreClass : "", + axis: "vertical", + width: 0, + height: 0, + layout: undefined, + globalLayoutProperties: {}, + }, options || {}); + + this.id = "epubjs-view:" + uuid(); + this.section = section; + this.index = section.index; + + this.element = this.container(this.settings.axis); + + this.added = false; + this.displayed = false; + this.rendered = false; + + this.width = this.settings.width; + this.height = this.settings.height; + + this.fixedWidth = 0; + this.fixedHeight = 0; + + // Blank Cfi for Parsing + this.epubcfi = new EpubCFI(); + + this.layout = this.settings.layout; + // Dom events to listen for + // this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"]; + + } + + container(axis) { + var element = document.createElement("div"); + + element.classList.add("epub-view"); + + // if(this.settings.axis === "horizontal") { + // element.style.width = "auto"; + // element.style.height = "0"; + // } else { + // element.style.width = "0"; + // element.style.height = "auto"; + // } + + element.style.overflow = "hidden"; + + if(axis && axis == "horizontal"){ + element.style.display = "inline-block"; + } else { + element.style.display = "block"; + } + + return element; + } + + create() { + + if(this.frame) { + return this.frame; + } + + if(!this.element) { + this.element = this.createContainer(); + } + + this.frame = document.createElement("div"); + this.frame.id = this.id; + this.frame.style.overflow = "hidden"; + this.frame.style.wordSpacing = "initial"; + this.frame.style.lineHeight = "initial"; + + this.resizing = true; + + // this.frame.style.display = "none"; + this.element.style.visibility = "hidden"; + this.frame.style.visibility = "hidden"; + + if(this.settings.axis === "horizontal") { + this.frame.style.width = "auto"; + this.frame.style.height = "0"; + } else { + this.frame.style.width = "0"; + this.frame.style.height = "auto"; + } + + this._width = 0; + this._height = 0; + + this.element.appendChild(this.frame); + this.added = true; + + this.elementBounds = bounds(this.element); + + return this.frame; + } + + render(request, show) { + + // view.onLayout = this.layout.format.bind(this.layout); + this.create(); + + // Fit to size of the container, apply padding + this.size(); + + // Render Chain + return this.section.render(request) + .then(function(contents){ + return this.load(contents); + }.bind(this)) + // .then(function(doc){ + // return this.hooks.content.trigger(view, this); + // }.bind(this)) + .then(function(){ + // this.settings.layout.format(view.contents); + // return this.hooks.layout.trigger(view, this); + }.bind(this)) + // .then(function(){ + // return this.display(); + // }.bind(this)) + // .then(function(){ + // return this.hooks.render.trigger(view, this); + // }.bind(this)) + .then(function(){ + + // apply the layout function to the contents + this.settings.layout.format(this.contents); + + // Expand the iframe to the full size of the content + // this.expand(); + + // Listen for events that require an expansion of the iframe + this.addListeners(); + + if(show !== false) { + //this.q.enqueue(function(view){ + this.show(); + //}, view); + } + // this.map = new Map(view, this.layout); + //this.hooks.show.trigger(view, this); + this.emit(EVENTS.VIEWS.RENDERED, this.section); + + }.bind(this)) + .catch(function(e){ + this.emit(EVENTS.VIEWS.LOAD_ERROR, e); + }.bind(this)); + + } + + // Determine locks base on settings + size(_width, _height) { + var width = _width || this.settings.width; + var height = _height || this.settings.height; + + if(this.layout.name === "pre-paginated") { + // TODO: check if these are different than the size set in chapter + this.lock("both", width, height); + } else if(this.settings.axis === "horizontal") { + this.lock("height", width, height); + } else { + this.lock("width", width, height); + } + + } + + // Lock an axis to element dimensions, taking borders into account + lock(what, width, height) { + var elBorders = borders(this.element); + var iframeBorders; + + if(this.frame) { + iframeBorders = borders(this.frame); + } else { + iframeBorders = {width: 0, height: 0}; + } + + if(what == "width" && isNumber(width)){ + this.lockedWidth = width - elBorders.width - iframeBorders.width; + this.resize(this.lockedWidth, false); // width keeps ratio correct + } + + if(what == "height" && isNumber(height)){ + this.lockedHeight = height - elBorders.height - iframeBorders.height; + this.resize(false, this.lockedHeight); + } + + if(what === "both" && + isNumber(width) && + isNumber(height)){ + + this.lockedWidth = width - elBorders.width - iframeBorders.width; + this.lockedHeight = height - elBorders.height - iframeBorders.height; + + this.resize(this.lockedWidth, this.lockedHeight); + } + + } + + // Resize a single axis based on content dimensions + expand(force) { + var width = this.lockedWidth; + var height = this.lockedHeight; + + var textWidth, textHeight; + + if(!this.frame || this._expanding) return; + + this._expanding = true; + + // Expand Horizontally + if(this.settings.axis === "horizontal") { + width = this.contentWidth(textWidth); + } // Expand Vertically + else if(this.settings.axis === "vertical") { + height = this.contentHeight(textHeight); + } + + // Only Resize if dimensions have changed or + // if Frame is still hidden, so needs reframing + if(this._needsReframe || width != this._width || height != this._height){ + this.resize(width, height); + } + + this._expanding = false; + } + + contentWidth(min) { + return this.frame.scrollWidth; + } + + contentHeight(min) { + return this.frame.scrollHeight; + } + + + resize(width, height) { + + if(!this.frame) return; + + if(isNumber(width)){ + this.frame.style.width = width + "px"; + this._width = width; + } + + if(isNumber(height)){ + this.frame.style.height = height + "px"; + this._height = height; + } + + this.prevBounds = this.elementBounds; + + this.elementBounds = bounds(this.element); + + let size = { + width: this.elementBounds.width, + height: this.elementBounds.height, + widthDelta: this.elementBounds.width - this.prevBounds.width, + heightDelta: this.elementBounds.height - this.prevBounds.height, + }; + + this.onResize(this, size); + + this.emit(EVENTS.VIEWS.RESIZED, size); + + } + + + load(contents) { + var loading = new defer(); + var loaded = loading.promise; + var doc = parse(contents, "text/html"); + var body = qs(doc, "body"); + + /* + var srcs = doc.querySelectorAll("[src]"); + + Array.prototype.slice.call(srcs) + .forEach(function(item) { + var src = item.getAttribute("src"); + var assetUri = URI(src); + var origin = assetUri.origin(); + var absoluteUri; + + if (!origin) { + absoluteUri = assetUri.absoluteTo(this.section.url); + item.src = absoluteUri; + } + }.bind(this)); + */ + this.frame.innerHTML = body.innerHTML; + + this.document = this.frame.ownerDocument; + this.window = this.document.defaultView; + + this.contents = new Contents(this.document, this.frame); + + this.rendering = false; + + loading.resolve(this.contents); + + + return loaded; + } + + setLayout(layout) { + this.layout = layout; + } + + + resizeListenters() { + // Test size again + // clearTimeout(this.expanding); + // this.expanding = setTimeout(this.expand.bind(this), 350); + } + + addListeners() { + //TODO: Add content listeners for expanding + } + + removeListeners(layoutFunc) { + //TODO: remove content listeners for expanding + } + + display(request) { + var displayed = new defer(); + + if (!this.displayed) { + + this.render(request).then(function () { + + this.emit(EVENTS.VIEWS.DISPLAYED, this); + this.onDisplayed(this); + + this.displayed = true; + + displayed.resolve(this); + + }.bind(this)); + + } else { + displayed.resolve(this); + } + + + return displayed.promise; + } + + show() { + + this.element.style.visibility = "visible"; + + if(this.frame){ + this.frame.style.visibility = "visible"; + } + + this.emit(EVENTS.VIEWS.SHOWN, this); + } + + hide() { + // this.frame.style.display = "none"; + this.element.style.visibility = "hidden"; + this.frame.style.visibility = "hidden"; + + this.stopExpanding = true; + this.emit(EVENTS.VIEWS.HIDDEN, this); + } + + position() { + return this.element.getBoundingClientRect(); + } + + locationOf(target) { + var parentPos = this.frame.getBoundingClientRect(); + var targetPos = this.contents.locationOf(target, this.settings.ignoreClass); + + return { + "left": window.scrollX + parentPos.left + targetPos.left, + "top": window.scrollY + parentPos.top + targetPos.top + }; + } + + onDisplayed(view) { + // Stub, override with a custom functions + } + + onResize(view, e) { + // Stub, override with a custom functions + } + + bounds() { + if(!this.elementBounds) { + this.elementBounds = bounds(this.element); + } + return this.elementBounds; + } + + destroy() { + + if(this.displayed){ + this.displayed = false; + + this.removeListeners(); + + this.stopExpanding = true; + this.element.removeChild(this.frame); + this.displayed = false; + this.frame = null; + + this._textWidth = null; + this._textHeight = null; + this._width = null; + this._height = null; + } + // this.element.style.height = "0px"; + // this.element.style.width = "0px"; + } +} + +EventEmitter(InlineView.prototype); + +export default InlineView; -- cgit v1.2.3