'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.Underline = exports.Highlight = exports.Mark = exports.Pane = undefined; var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _svg = require('./svg'); var _svg2 = _interopRequireDefault(_svg); var _events = require('./events'); var _events2 = _interopRequireDefault(_events); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Pane = exports.Pane = function () { function Pane(target) { var container = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document.body; _classCallCheck(this, Pane); this.target = target; this.element = _svg2.default.createElement('svg'); this.marks = []; // Match the coordinates of the target element this.element.style.position = 'absolute'; // Disable pointer events this.element.setAttribute('pointer-events', 'none'); // Set up mouse event proxying between the target element and the marks _events2.default.proxyMouse(this.target, this.marks); this.container = container; this.container.appendChild(this.element); this.render(); } _createClass(Pane, [{ key: 'addMark', value: function addMark(mark) { var g = _svg2.default.createElement('g'); this.element.appendChild(g); mark.bind(g, this.container); this.marks.push(mark); mark.render(); return mark; } }, { key: 'removeMark', value: function removeMark(mark) { var idx = this.marks.indexOf(mark); if (idx === -1) { return; } var el = mark.unbind(); this.element.removeChild(el); this.marks.splice(idx, 1); } }, { key: 'render', value: function render() { setCoords(this.element, coords(this.target, this.container)); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = this.marks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var m = _step.value; m.render(); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } }]); return Pane; }(); var Mark = exports.Mark = function () { function Mark() { _classCallCheck(this, Mark); this.element = null; } _createClass(Mark, [{ key: 'bind', value: function bind(element, container) { this.element = element; this.container = container; } }, { key: 'unbind', value: function unbind() { var el = this.element; this.element = null; return el; } }, { key: 'render', value: function render() {} }, { key: 'dispatchEvent', value: function dispatchEvent(e) { if (!this.element) return; this.element.dispatchEvent(e); } }, { key: 'getBoundingClientRect', value: function getBoundingClientRect() { return this.element.getBoundingClientRect(); } }, { key: 'getClientRects', value: function getClientRects() { var rects = []; var el = this.element.firstChild; while (el) { rects.push(el.getBoundingClientRect()); el = el.nextSibling; } return rects; } }, { key: 'filteredRanges', value: function filteredRanges() { var rects = Array.from(this.range.getClientRects()); // De-duplicate the boxes return rects.filter(function (box) { for (var i = 0; i < rects.length; i++) { if (rects[i] === box) { return true; } var contained = contains(rects[i], box); if (contained) { return false; } } return true; }); } }]); return Mark; }(); var Highlight = exports.Highlight = function (_Mark) { _inherits(Highlight, _Mark); function Highlight(range, className, data, attributes) { _classCallCheck(this, Highlight); var _this = _possibleConstructorReturn(this, (Highlight.__proto__ || Object.getPrototypeOf(Highlight)).call(this)); _this.range = range; _this.className = className; _this.data = data || {}; _this.attributes = attributes || {}; return _this; } _createClass(Highlight, [{ key: 'bind', value: function bind(element, container) { _get(Highlight.prototype.__proto__ || Object.getPrototypeOf(Highlight.prototype), 'bind', this).call(this, element, container); for (var attr in this.data) { if (this.data.hasOwnProperty(attr)) { this.element.dataset[attr] = this.data[attr]; } } for (var attr in this.attributes) { if (this.attributes.hasOwnProperty(attr)) { this.element.setAttribute(attr, this.attributes[attr]); } } if (this.className) { this.element.classList.add(this.className); } } }, { key: 'render', value: function render() { // Empty element while (this.element.firstChild) { this.element.removeChild(this.element.firstChild); } var docFrag = this.element.ownerDocument.createDocumentFragment(); var filtered = this.filteredRanges(); var offset = this.element.getBoundingClientRect(); var container = this.container.getBoundingClientRect(); for (var i = 0, len = filtered.length; i < len; i++) { var r = filtered[i]; var el = _svg2.default.createElement('rect'); el.setAttribute('x', r.left - offset.left + container.left); el.setAttribute('y', r.top - offset.top + container.top); el.setAttribute('height', r.height); el.setAttribute('width', r.width); docFrag.appendChild(el); } this.element.appendChild(docFrag); } }]); return Highlight; }(Mark); var Underline = exports.Underline = function (_Highlight) { _inherits(Underline, _Highlight); function Underline(range, className, data, attributes) { _classCallCheck(this, Underline); return _possibleConstructorReturn(this, (Underline.__proto__ || Object.getPrototypeOf(Underline)).call(this, range, className, data, attributes)); } _createClass(Underline, [{ key: 'render', value: function render() { // Empty element while (this.element.firstChild) { this.element.removeChild(this.element.firstChild); } var docFrag = this.element.ownerDocument.createDocumentFragment(); var filtered = this.filteredRanges(); var offset = this.element.getBoundingClientRect(); var container = this.container.getBoundingClientRect(); for (var i = 0, len = filtered.length; i < len; i++) { var r = filtered[i]; var rect = _svg2.default.createElement('rect'); rect.setAttribute('x', r.left - offset.left + container.left); rect.setAttribute('y', r.top - offset.top + container.top); rect.setAttribute('height', r.height); rect.setAttribute('width', r.width); rect.setAttribute('fill', 'none'); var line = _svg2.default.createElement('line'); line.setAttribute('x1', r.left - offset.left + container.left); line.setAttribute('x2', r.left - offset.left + container.left + r.width); line.setAttribute('y1', r.top - offset.top + container.top + r.height - 1); line.setAttribute('y2', r.top - offset.top + container.top + r.height - 1); line.setAttribute('stroke-width', 1); line.setAttribute('stroke', 'black'); //TODO: match text color? line.setAttribute('stroke-linecap', 'square'); docFrag.appendChild(rect); docFrag.appendChild(line); } this.element.appendChild(docFrag); } }]); return Underline; }(Highlight); function coords(el, container) { var offset = container.getBoundingClientRect(); var rect = el.getBoundingClientRect(); return { top: rect.top - offset.top, left: rect.left - offset.left, height: el.scrollHeight, width: el.scrollWidth }; } function setCoords(el, coords) { el.style.setProperty('top', coords.top + 'px', 'important'); el.style.setProperty('left', coords.left + 'px', 'important'); el.style.setProperty('height', coords.height + 'px', 'important'); el.style.setProperty('width', coords.width + 'px', 'important'); } function contains(rect1, rect2) { return rect2.right <= rect1.right && rect2.left >= rect1.left && rect2.top >= rect1.top && rect2.bottom <= rect1.bottom; }