jodit
Version:
Jodit is awesome and usefully wysiwyg editor with filebrowser
879 lines (714 loc) • 24.2 kB
JavaScript
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2020 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
describe('Selection Module Tests', function() {
describe('Current method', function() {
describe('Cursor outside the editor', function() {
it('Should return false', function() {
const editor = getJodit(),
div = document.createElement('div');
div.innerHTML = 'test';
document.body.appendChild(div);
editor.value = '<h1>test <span>test</span>sdfsdfds</h1>';
const range = document.createRange();
range.setStart(div.firstChild, 1);
range.setEnd(div.firstChild, 2);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
expect(editor.s.current()).is.null;
document.body.removeChild(div);
});
});
describe('Cursor in the left of some SPAN', function() {
it('Should return text before this span', function() {
const editor = getJodit();
editor.value = '<h1>one<span>two</span>tree</h1>';
const range = editor.s.createRange();
range.setStart(editor.editor.firstChild, 1);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.current()).equals(
editor.editor.firstChild.firstChild
); //one
});
});
describe('Cursor inside the text node ', function() {
it('Should return text', function() {
const editor = getJodit();
editor.value = '<h1>test</h1>';
const range = editor.s.createRange();
range.setStart(editor.editor.firstChild.firstChild, 1);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.current()).equals(
editor.editor.firstChild.firstChild
); // test
});
});
describe('Cursor after h1', function() {
it('Should return text inside h1', function() {
const editor = getJodit();
editor.value = '<h1>test</h1>';
const range = editor.s.createRange();
range.setStart(editor.editor, 1);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.current()).equals(
editor.editor.firstChild.firstChild
); // test
});
describe('With false argument', function() {
it('Should return h1', function() {
const editor = getJodit();
editor.value = '<h1>test</h1>';
const range = editor.s.createRange();
range.setStart(editor.editor, 1);
range.collapse(true);
editor.s.selectRange(range);
expect([
editor.editor.firstChild,
editor.editor.firstChild.firstChild
]).to.include(editor.s.current(false)); // h1
});
});
});
describe('Select img', function() {
it('Should return this image', function() {
const editor = getJodit();
editor.value = '<h1>test <img src="#" alt=""> sdfsdfs</h1>';
const range = editor.s.createRange();
range.selectNode(editor.editor.querySelector('img'));
editor.s.selectRange(range);
expect(editor.s.current()).equals(
editor.editor.querySelector('img')
);
});
});
});
describe('cursorInTheEdge', function() {
describe('Cursor in the text', function() {
describe('cursorOnTheLeft and cursorOnTheRight', function() {
describe('Cursor inside P but inside Li', function() {
describe('Cursor in the end of text node', function() {
it('Should work correct', function() {
const editor = getJodit();
editor.value = '<ul><li><p>test</p></li></ul>';
const range = editor.s.createRange();
range.setStartAfter(
editor.editor.querySelector('p').firstChild
);
range.collapse(true);
editor.s.selectRange(range);
['li', 'p'].forEach(function(tag) {
expect(
editor.s.cursorOnTheLeft(
editor.editor.querySelector(tag)
)
).is.false;
expect(
editor.s.cursorOnTheRight(
editor.editor.querySelector(tag)
)
).is.true;
});
});
});
describe('Cursor in the start of text node', function() {
it('Should work correct', function() {
const editor = getJodit();
editor.value = '<ul><li><p>test</p></li></ul>';
const range = editor.s.createRange();
range.setStartBefore(
editor.editor.querySelector('p').firstChild
);
range.collapse(true);
editor.s.selectRange(range);
['li', 'p'].forEach(function(tag) {
expect(
editor.s.cursorOnTheLeft(
editor.editor.querySelector(tag)
)
).is.true;
expect(
editor.s.cursorOnTheRight(
editor.editor.querySelector(tag)
)
).is.false;
});
});
});
});
});
describe('Cursor in the end of text node but after this has BR', function() {
it('Should return true', function() {
const editor = getJodit();
editor.value = '<p>test<br></p>';
const range = editor.s.createRange();
range.setStart(editor.editor.firstChild.firstChild, 4);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.cursorOnTheRight(editor.editor.firstChild))
.is.true;
});
});
describe('Cursor in the end of text node but after this has image', function() {
it('Should return false', function() {
const editor = getJodit();
editor.value = '<p>test<img/></p>';
const range = editor.s.createRange();
range.setStart(editor.editor.firstChild.firstChild, 4);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.cursorOnTheRight(editor.editor.firstChild))
.is.false;
});
});
describe('Cursor in the middle of text node', function() {
it('Should return false', function() {
const editor = getJodit();
editor.value = '<p>test</p>';
const range = editor.s.createRange();
range.setStart(editor.editor.firstChild.firstChild, 2);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.cursorOnTheRight(editor.editor.firstChild))
.is.false;
});
describe('Cursor in the middle of text node but after cursor only invisible spaces', function() {
it('Should return true', function() {
const editor = getJodit();
editor.value =
'<p>test' +
Jodit.INVISIBLE_SPACE +
Jodit.INVISIBLE_SPACE +
Jodit.INVISIBLE_SPACE +
'</p>';
const range = editor.s.createRange();
range.setStart(editor.editor.firstChild.firstChild, 4);
range.collapse(true);
editor.s.selectRange(range);
expect(
editor.s.cursorOnTheRight(editor.editor.firstChild)
).is.true;
});
});
describe('Cursor in the middle of text node but before cursor only invisible spaces', function() {
it('Should return true', function() {
const editor = getJodit();
editor.value =
'<p>' +
Jodit.INVISIBLE_SPACE +
Jodit.INVISIBLE_SPACE +
Jodit.INVISIBLE_SPACE +
'test</p>';
const range = editor.s.createRange();
range.setStart(editor.editor.firstChild.firstChild, 3);
range.collapse(true);
editor.s.selectRange(range);
expect(
editor.s.cursorOnTheLeft(editor.editor.firstChild)
).is.true;
});
});
describe('Cursor in the end of text node but after this has several not empty text nodes', function() {
it('Should return false', function() {
const editor = getJodit();
editor.value = '<p>test</p>';
const range = editor.s.createRange();
range.setStart(editor.editor.firstChild.firstChild, 4);
range.collapse(true);
editor.s.selectRange(range);
editor.s.insertNode(editor.createInside.text('a'));
range.setStart(editor.editor.firstChild.firstChild, 4);
range.collapse(true);
editor.s.selectRange(range);
expect(
editor.s.cursorOnTheRight(editor.editor.firstChild)
).is.false;
});
describe('Cursor in the end of text node and after are only text nodes with invisible spaces', function() {
it('Should return true', function() {
const editor = getJodit();
editor.value = '<p>test</p>';
const range = editor.s.createRange();
range.setStart(
editor.editor.firstChild.firstChild,
4
);
range.collapse(true);
editor.s.selectRange(range);
editor.s.insertNode(
editor.createInside.text(Jodit.INVISIBLE_SPACE)
);
editor.s.insertNode(
editor.createInside.text(Jodit.INVISIBLE_SPACE)
);
editor.s.insertNode(
editor.createInside.text(Jodit.INVISIBLE_SPACE)
);
range.setStart(
editor.editor.firstChild.firstChild,
4
);
range.collapse(true);
editor.s.selectRange(range);
expect(
editor.s.cursorOnTheRight(
editor.editor.firstChild
)
).is.true;
});
});
describe('Inverse', function() {
describe('Cursor in the start of text node but before this has several not empty text nodes', function() {
it('Should return false', function() {
const editor = getJodit();
editor.value = '<p>test</p>';
const range = editor.s.createRange();
range.setStart(
editor.editor.firstChild.firstChild,
0
);
range.collapse(true);
editor.s.selectRange(range);
editor.s.insertNode(
editor.createInside.text('a')
);
range.setStart(
editor.editor.firstChild.lastChild,
0
);
range.collapse(true);
editor.s.selectRange(range);
expect(
editor.s.cursorOnTheLeft(
editor.editor.firstChild
)
).is.false;
});
describe('Cursor in the start of text node and before are only text nodes with invisible spaces', function() {
it('Should return true', function() {
const editor = getJodit();
editor.value = '<p>test</p>';
const range = editor.s.createRange();
range.setStart(
editor.editor.firstChild.firstChild,
0
);
range.collapse(true);
editor.s.selectRange(range);
editor.s.insertNode(
editor.createInside.text(
Jodit.INVISIBLE_SPACE
)
);
editor.s.insertNode(
editor.createInside.text(
Jodit.INVISIBLE_SPACE
)
);
editor.s.insertNode(
editor.createInside.text(
Jodit.INVISIBLE_SPACE
)
);
range.setStart(
editor.editor.firstChild.lastChild,
0
);
range.collapse(true);
editor.s.selectRange(range);
expect(
editor.s.cursorOnTheLeft(
editor.editor.firstChild
)
).is.true;
});
});
});
});
});
});
});
describe('Cursor after element', function() {
it('Should return null', function() {
const editor = getJodit();
editor.value = '<p>test</p>';
const range = editor.s.createRange();
range.setStartAfter(editor.editor.firstChild);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.cursorOnTheRight(editor.editor.firstChild)).is
.null;
});
});
describe('Cursor before element', function() {
it('Should return null', function() {
const editor = getJodit();
editor.value = '<p>test</p>';
const range = editor.s.createRange();
range.setStartBefore(editor.editor.firstChild);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.cursorOnTheLeft(editor.editor.firstChild)).is
.null;
});
});
describe('Cursor in the start of element ', function() {
it('Should return true', function() {
const editor = getJodit();
editor.value = '<p><span>test</span></p>';
const range = editor.s.createRange();
range.setStartBefore(editor.editor.firstChild.firstChild);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.cursorOnTheLeft(editor.editor.firstChild)).is
.true;
});
});
describe('Cursor in the end of element ', function() {
it('Should return true', function() {
const editor = getJodit();
editor.value = '<p><span>test</span></p>';
const range = editor.s.createRange();
range.setStartAfter(editor.editor.firstChild.firstChild);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.cursorOnTheRight(editor.editor.firstChild)).is
.true;
});
});
describe('Cursor not in the end of element ', function() {
it('Should return false', function() {
const editor = getJodit();
editor.value = '<p><span>test</span><span>stop</span></p>';
const range = editor.s.createRange();
range.setStartAfter(editor.editor.firstChild.firstChild);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.cursorOnTheRight(editor.editor.firstChild)).is
.false;
});
});
describe('Cursor not in the start of element ', function() {
it('Should return false', function() {
const editor = getJodit();
editor.value = '<p><span>test</span><span>stop</span></p>';
const range = editor.s.createRange();
range.setStartAfter(editor.editor.firstChild.firstChild);
range.collapse(true);
editor.s.selectRange(range);
expect(editor.s.cursorOnTheLeft(editor.editor.firstChild)).is
.false;
});
});
describe('If cursor in the end of P', function() {
it('Should return true', function() {
const editor = getJodit();
editor.value = '<p>test</p>';
const sel = editor.s.sel,
range = editor.s.createRange();
range.setStart(editor.editor.firstChild.firstChild, 4);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
expect(editor.s.cursorOnTheRight(editor.editor.firstChild)).is
.true;
range.setStart(editor.editor.firstChild.firstChild, 2);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
expect(editor.s.cursorOnTheRight(editor.editor.firstChild)).is
.false;
});
});
describe('If cursor in the end of SPAN in the end of P', function() {
it('Should return true', function() {
const editor = getJodit();
editor.value = '<p>test<span>1</span></p>';
const sel = editor.s.sel,
range = editor.s.createRange();
range.selectNodeContents(editor.editor.firstChild.lastChild);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
expect(
editor.s.cursorInTheEdge(false, editor.editor.firstChild)
).is.true;
});
});
describe('Curson in the end of span inside P and check cursorInTheEdge(true)', function() {
it('Should return false', function() {
const editor = getJodit();
editor.value = '<p>Some <span>text</span></p>';
const sel = editor.s.sel,
range = editor.s.createRange();
range.selectNodeContents(editor.editor.firstChild.lastChild);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
expect(editor.s.cursorInTheEdge(true, editor.editor.firstChild))
.is.false;
});
});
});
describe('Change mode', function() {
it('Should restore collapsed selection when user change mode - from WYSIWYG to TEXTAREA', function() {
const editor = getJodit();
editor.value = '<p>test</p>';
const sel = editor.s.sel,
range = editor.s.createRange();
range.setStart(editor.editor.firstChild.firstChild, 2);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
editor.setMode(Jodit.MODE_SOURCE);
const mirror = editor.container.querySelector(
'textarea.jodit-source__mirror'
);
expect(mirror.value).equals('<p>test</p>');
expect(mirror.selectionStart).equals(5);
expect(mirror.selectionEnd).equals(5);
}).timeout(6000);
it('Should restore collapsed selection when user change mode - from WYSIWYG to TEXTAREA for long string', function(done) {
unmockPromise();
let timeout;
const __done = function() {
clearTimeout(timeout);
done();
};
timeout = setTimeout(function() {
expect(false).is.true;
__done();
}, 140100);
Jodit.make(appendTestArea(), {
defaultMode: Jodit.MODE_SOURCE,
sourceEditor: 'ace',
beautifyHTML: false,
events: {
/**
* @this Events
*/
sourceEditorReady: function(jodit) {
jodit.setMode(Jodit.MODE_WYSIWYG);
jodit.setEditorValue(
('<p>' + 'test '.repeat(50) + '</p>').repeat(1)
);
const sel = jodit.ew.getSelection(),
range = jodit.ed.createRange();
range.selectNodeContents(
jodit.editor.querySelector('p')
);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
jodit.s.insertHTML('hello');
jodit.setMode(Jodit.MODE_SOURCE);
const ace =
jodit.__plugins.source.sourceEditor.instance;
expect(ace).not.null;
expect(ace.getSelectionRange().start.column).equals(
258
);
expect(ace.getSelectionRange().start.row).equals(0);
ace.session.insert(ace.getCursorPosition(), ' world');
expect(
jodit.__plugins.source.sourceEditor.getValue()
).equals(
'<p>' + 'test '.repeat(49) + 'test hello world</p>'
);
mockPromise();
__done();
}
}
});
}).timeout(116000);
it('Should restore collapsed selection when user change mode - from TEXTAREA to WYSIWYG', function() {
const editor = getJodit({
useAceEditor: false,
defaultMode: Jodit.MODE_SOURCE
});
editor.value = '<p>test</p>';
const mirror = editor.container.querySelector(
'textarea.jodit-source__mirror'
);
mirror.setSelectionRange(5, 5);
editor.setMode(Jodit.MODE_WYSIWYG);
editor.s.insertNode(editor.createInside.text(' a '));
expect(editor.value).equals('<p>te a st</p>');
});
it('Should restore non collapsed selection when user change mode - from WYSIWYG to TEXTAREA', function() {
const editor = getJodit({
useAceEditor: false
});
editor.value = '<p>test</p>';
const sel = editor.s.sel,
range = editor.s.createRange();
range.setStart(editor.editor.firstChild.firstChild, 1);
range.setEnd(editor.editor.firstChild.firstChild, 3);
sel.removeAllRanges();
sel.addRange(range);
editor.setMode(Jodit.MODE_SOURCE);
const mirror = editor.container.querySelector(
'textarea.jodit-source__mirror'
);
expect(mirror.value).equals('<p>test</p>');
expect(mirror.selectionStart).equals(4);
expect(mirror.selectionEnd).equals(6);
});
describe('Problem', function() {
it('Should restore non collapsed selection when user change mode - from TEXTAREA to WYSIWYG', function() {
const editor = getJodit({
useAceEditor: false,
defaultMode: Jodit.MODE_SOURCE
});
editor.s.focus();
editor.value = '<p>test</p>';
const mirror = editor.container.querySelector(
'textarea.jodit-source__mirror'
);
mirror.setSelectionRange(2, 8);
editor.setMode(Jodit.MODE_WYSIWYG);
expect(editor.s.isCollapsed()).is.false;
editor.s.insertNode(editor.createInside.text(' a '));
expect(editor.value).equals('<p> a </p>');
});
});
it('Should restore collapsed selection inside empty element - from TEXTAREA to WYSIWYG', function() {
const editor = getJodit({
useAceEditor: false,
defaultMode: Jodit.MODE_SOURCE
});
editor.value = '<p><a>11</a></p>';
const mirror = editor.container.querySelector(
'textarea.jodit-source__mirror'
);
mirror.setSelectionRange(7, 7);
editor.setMode(Jodit.MODE_WYSIWYG);
expect(editor.s.isCollapsed()).is.true;
editor.s.insertNode(editor.createInside.text(' a '));
expect(editor.value).equals('<p><a>1 a 1</a></p>');
});
});
describe('Click on empty tag', function() {
it('Should move cursore inside that', function() {
const editor = getJodit();
editor.value = '<p></p><p></p><p></p>';
simulateEvent(
'mousedown',
0,
editor.editor.getElementsByTagName('p')[1]
);
editor.s.insertHTML('test');
expect('<p></p><p>test</p><p></p>').equals(editor.value);
});
});
describe('Method setCursorIn', function() {
describe('Call for not Node element', function() {
it('Should throw exception', function() {
const editor = getJodit();
editor.value = '<p>1</p><p>2</p>';
expect(function() {
editor.s.setCursorIn(editor.editor.querySelector('strong'));
}).to.throw();
});
describe('Call for inserted fragment', function() {
it('Should not throw exception', function() {
const editor = getJodit();
editor.value = '<p>1<span>3</span>2</p>';
editor.s.select(editor.editor.querySelector('span'));
const fragment = editor.s.range.extractContents();
editor.s.insertNode(fragment);
});
});
});
describe('Call for element what is not inside the current editor', function() {
it('Should throw exception', function() {
const editor = getJodit();
expect(function() {
editor.s.setCursorIn(document.body);
}).to.throw();
});
});
it('Should move cursor inside node in the end', function() {
const editor = getJodit();
editor.value = '<p>1</p><p>2</p>';
editor.s.setCursorIn(editor.editor.lastChild);
editor.s.insertHTML('test');
expect(editor.value).equals('<p>1</p><p>2test</p>');
});
describe('With inStart = true', function() {
it('Should move cursor inside node in the start', function() {
const editor = getJodit();
editor.value = '<p>1</p><p>2</p>';
editor.s.setCursorIn(editor.editor.lastChild, true);
editor.s.insertHTML('test');
expect(editor.value).equals('<p>1</p><p>test2</p>');
});
});
});
describe('Method eachSelection', function() {
it('Should call callback for each node in selection', function() {
const editor = getJodit({
disablePlugins: ['WrapTextNodes']
});
editor.value =
'<p>1</p><p>2</p><strong><span>22</span></strong><p>4</p>stop';
const range = editor.s.createRange();
range.setStartBefore(editor.editor.firstChild);
range.setEndAfter(editor.editor.lastChild);
editor.s.selectRange(range);
const nodesNames = [];
editor.s.eachSelection(function(node) {
nodesNames.push(node.nodeName);
});
expect(
['P', 'P', 'STRONG', 'P', '#text'].toString().toLowerCase()
).equals(nodesNames.toString().toLowerCase());
});
it('Should call callback for each node in selection range', function() {
const editor = getJodit({ disablePlugins: ['WrapTextNodes'] });
editor.value =
'<p>1</p><p>2</p><strong><span>22</span></strong><p>4</p>stop';
const range = editor.s.createRange();
range.setStartBefore(editor.editor.firstChild.nextSibling);
range.setEndAfter(editor.editor.lastChild.previousSibling);
editor.s.selectRange(range);
const nodesNames = [];
editor.s.eachSelection(function(node) {
nodesNames.push(node.nodeName);
});
expect(['p', 'strong', 'p'].toString().toLowerCase()).equals(
nodesNames.toString().toLowerCase()
);
});
it('Should not call callback for editor', function() {
const editor = getJodit();
editor.value = '';
editor.s.setCursorIn(editor.editor);
const nodesNames = [];
editor.s.eachSelection(function(node) {
nodesNames.push(node.nodeName);
});
expect(['#text'].toString().toLowerCase()).equals(
nodesNames.toString().toLowerCase()
);
});
it('Should call callback for current node if selection is collapsed', function() {
const editor = getJodit();
editor.value = '<p>1</p><p>2</p>';
editor.s.setCursorIn(editor.editor.firstChild);
const nodesNames = [];
editor.s.eachSelection(function(node) {
nodesNames.push(node.nodeName);
});
expect(['#text'].toString().toLowerCase()).equals(
nodesNames.toString().toLowerCase()
);
});
describe('If selected element is UL or LI or content in LI', function() {});
});
});