New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@openstax/highlighter

Package Overview
Dependencies
Maintainers
5
Versions
56
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@openstax/highlighter - npm Package Compare versions

Comparing version 1.6.3 to 1.7.0-alpha1

7

dist/Highlighter.d.ts
import Highlight, { IOptions as HighlightOptions } from './Highlight';
import SerializedHighlight from './SerializedHighlight';
export declare const ON_SELECT_DELAY = 500;
interface IOptions {

@@ -16,2 +17,4 @@ snapTableRows?: boolean;

private options;
private selectionTimeout;
private previousRange;
constructor(container: HTMLElement, options?: IOptions);

@@ -32,6 +35,8 @@ unmount(): void;

readonly document: Document;
private onMouseup;
private onSelectionChange;
private onClickHandler;
private onClick;
private onSelect;
private compareRanges;
}
export {};

@@ -10,5 +10,8 @@ "use strict";

const SerializedHighlight_1 = require("./SerializedHighlight");
exports.ON_SELECT_DELAY = 500;
class Highlighter {
constructor(container, options = {}) {
this.highlights = {};
this.selectionTimeout = null;
this.previousRange = null;
this.eraseAll = () => {

@@ -25,20 +28,34 @@ this.getHighlights().forEach(this.erase);

};
this.onMouseup = (ev) => {
this.onSelectionChange = () => {
const selection = this.document.getSelection();
if (!selection) {
if (!selection
|| selection.isCollapsed
|| selection.type === 'None'
|| !dom_1.default(this.container).contains(selection.anchorNode)
|| !dom_1.default(this.container).contains(selection.focusNode)
|| this.compareRanges(selection ? selection_1.getRange(selection) : null, this.previousRange)) {
return;
}
if (selection.isCollapsed) {
this.onClick(ev);
if (this.selectionTimeout) {
clearTimeout(this.selectionTimeout);
}
else {
this.onSelect(selection);
}
this.selectionTimeout = setTimeout(() => {
const sel = this.document.getSelection();
if (!sel) {
return;
}
this.onSelect(sel);
}, exports.ON_SELECT_DELAY);
};
this.onClickHandler = (event) => {
this.onClick(event);
};
this.container = container;
this.options = Object.assign({ className: 'highlight' }, options);
this.container.addEventListener('mouseup', this.onMouseup);
this.container.addEventListener('click', this.onClickHandler);
document.addEventListener('selectionchange', this.onSelectionChange);
}
unmount() {
this.container.removeEventListener('mouseup', this.onMouseup);
this.container.removeEventListener('click', this.onClickHandler);
document.removeEventListener('selectionchange', this.onSelectionChange);
}

@@ -120,2 +137,3 @@ highlight(highlight) {

const range = selection_1.snapSelection(selection, this.options);
this.previousRange = range || null;
if (onSelect && range) {

@@ -133,4 +151,24 @@ const highlights = Object.values(this.highlights)

}
compareRanges(range1, range2) {
if (range1 === null && range2 === null) {
return true;
}
if (range1 === null && range2) {
return false;
}
if (range2 === null && range1) {
return false;
}
if (range1
&& range2
&& range1.startContainer === range2.startContainer
&& range1.endContainer === range2.endContainer
&& range1.startOffset === range2.startOffset
&& range1.endOffset === range2.endOffset) {
return true;
}
return false;
}
}
exports.default = Highlighter;
//# sourceMappingURL=Highlighter.js.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Highlight_1 = require("./Highlight");
const Highlighter_1 = require("./Highlighter");
const injectHighlightWrappersUtils = require("./injectHighlightWrappers");
const rangeContents = require("./rangeContents");

@@ -41,2 +43,45 @@ const selection = require("./selection");

});
describe('onClick', () => {
test('handle clicks inside of the container', () => {
const spyOnClick = jest.fn();
const container = document.createElement('div');
// tslint:disable-next-line no-unused-expression
new Highlighter_1.default(container, { onClick: spyOnClick });
const e = document.createEvent('MouseEvent');
e.initEvent('click', true, true);
container.dispatchEvent(e);
expect(spyOnClick).toHaveBeenCalledWith(undefined, e);
});
test('handle clicks on the highlights', () => {
const spyOnClick = jest.fn();
const spyInjectHighlightWrappersUtils = jest.fn();
const container = document.createElement('div');
const highlightElement = document.createElement('span');
highlightElement.setAttribute(injectHighlightWrappersUtils.DATA_ATTR, 'highlight');
highlightElement.setAttribute(injectHighlightWrappersUtils.DATA_ID_ATTR, 'some-highlight');
container.append(highlightElement);
const highlight = new Highlight_1.default(new Range(), { id: 'some-highlight', content: 'asd' });
jest.spyOn(injectHighlightWrappersUtils, 'default')
.mockImplementation(spyInjectHighlightWrappersUtils);
// tslint:disable-next-line no-unused-expression
const highlighter = new Highlighter_1.default(container, { onClick: spyOnClick });
highlighter.highlight(highlight);
const e = document.createEvent('MouseEvent');
e.initEvent('click', true, true);
Object.defineProperty(e, 'target', { value: highlightElement });
container.dispatchEvent(e);
expect(spyInjectHighlightWrappersUtils).toHaveBeenCalledWith(highlight, expect.anything());
expect(spyOnClick).toHaveBeenCalledWith(highlight, e);
});
test('does not handle clicks outside of the container', () => {
const spyOnClick = jest.fn();
const container = document.createElement('div');
// tslint:disable-next-line no-unused-expression
new Highlighter_1.default(container, { onClick: spyOnClick });
const e = document.createEvent('MouseEvent');
e.initEvent('click', true, true);
document.dispatchEvent(e);
expect(spyOnClick).not.toHaveBeenCalled();
});
});
describe('onSelect', () => {

@@ -46,2 +91,3 @@ let getSelectionSpy;

let rangeContentsStringSpy;
jest.useFakeTimers();
beforeEach(() => {

@@ -60,13 +106,25 @@ getSelectionSpy = jest.spyOn(document, 'getSelection');

let highlight;
const highlighter = new Highlighter_1.default(document.createElement('div'), { onSelect: (_, newHighlight) => highlight = newHighlight });
const container = document.createElement('div');
const node = document.createElement('div');
node.innerHTML = 'some text';
container.appendChild(node);
// tslint:disable-next-line no-unused-expression
new Highlighter_1.default(container, { onSelect: (_, newHighlight) => highlight = newHighlight });
const inputSelection = new Selection();
const selectionRange = new Range();
const snappedRange = new Range();
selectionRange.setStart(node, 0);
selectionRange.setEnd(node, 5);
selectionRange.setStart(node, 0);
selectionRange.setEnd(node, 5);
Object.defineProperty(inputSelection, 'isCollapsed', { value: false });
Object.defineProperty(inputSelection, 'anchorNode', { value: node });
Object.defineProperty(inputSelection, 'focusNode', { value: node });
inputSelection.getRangeAt = jest.fn(() => selectionRange);
snapSelectionSpy.mockImplementation(() => snappedRange);
getSelectionSpy.mockImplementation(() => inputSelection);
const e = document.createEvent('MouseEvents');
e.initEvent('mouseup', true, true);
highlighter.container.dispatchEvent(e);
const e = document.createEvent('Event');
e.initEvent('selectionchange', true, true);
document.dispatchEvent(e);
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY);
if (highlight === undefined) {

@@ -80,3 +138,41 @@ expect(highlight).toBeDefined();

});
it('noops on selecitonchange event if there is no selection, selection is collapsed or selection type is None', () => {
const spyOnSelect = jest.fn();
const container = document.createElement('div');
const node = document.createElement('div');
container.appendChild(node);
// tslint:disable-next-line no-unused-expression
new Highlighter_1.default(container, { onSelect: spyOnSelect });
getSelectionSpy.mockImplementation(() => null);
const e = document.createEvent('Event');
e.initEvent('selectionchange', true, true);
document.dispatchEvent(e);
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY + 100);
expect(spyOnSelect).not.toHaveBeenCalled();
getSelectionSpy.mockImplementation(() => ({ isCollapsed: true }));
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY + 100);
expect(spyOnSelect).not.toHaveBeenCalled();
getSelectionSpy.mockImplementation(() => ({ isCollapsed: false, type: 'None' }));
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY + 100);
expect(spyOnSelect).not.toHaveBeenCalled();
});
it('noops on selecitonchange event if anchorNode or focusNode is not in the container', () => {
const spyOnSelect = jest.fn();
const container = document.createElement('div');
const nodeInside = document.createElement('div');
const nodeOutside = document.createElement('div');
container.appendChild(nodeInside);
// tslint:disable-next-line no-unused-expression
new Highlighter_1.default(container, { onSelect: spyOnSelect });
getSelectionSpy.mockImplementation(() => ({ isCollapsed: false, anchorNode: nodeOutside, focusNode: nodeInside }));
const e = document.createEvent('Event');
e.initEvent('selectionchange', true, true);
document.dispatchEvent(e);
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY + 100);
expect(spyOnSelect).not.toHaveBeenCalled();
getSelectionSpy.mockImplementation(() => ({ isCollapsed: false, anchorNode: nodeInside, focusNode: nodeOutside }));
jest.runTimersToTime(Highlighter_1.ON_SELECT_DELAY + 100);
expect(spyOnSelect).not.toHaveBeenCalled();
});
});
//# sourceMappingURL=Highlighter.test.js.map

4

package.json
{
"name": "@openstax/highlighter",
"version": "1.6.3",
"version": "1.7.0-alpha1",
"main": "dist/index.js",

@@ -71,3 +71,3 @@ "license": "MIT",

"dependencies": {
"@openstax/highlights-client": "0.2.2",
"@openstax/highlights-client": "0.2.1",
"change-case": "^4.0.0",

@@ -74,0 +74,0 @@ "serialize-selection": "^1.1.1",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc