import { context, describe, it } from '@ephox/bedrock-client';
import { Type } from '@ephox/katamari';
import { ContentEditable, Hierarchy, Insert, Remove, SugarBody, SugarElement } from '@ephox/sugar';
import { assert } from 'chai';
import DOMUtils from 'tinymce/core/api/dom/DOMUtils';
import Schema from 'tinymce/core/api/html/Schema';
import Tools from 'tinymce/core/api/util/Tools';
import * as HtmlUtils from '../../module/test/HtmlUtils';
describe('browser.tinymce.core.dom.DOMUtilsTest', () => {
const DOM = DOMUtils(document, { keep_values: true, schema: Schema() });
const getTestElement = (id: string = 'test') =>
DOM.get(id) as HTMLElement;
it('parseStyle', () => {
DOM.add(document.body, 'div', { id: 'test' });
const dom = DOMUtils(document, {
keep_values: true,
url_converter: (u) => {
return 'X' + u + 'Y';
}
});
assert.equal(dom.serializeStyle(dom.parseStyle('border: 1px solid red; color: green')), 'border: 1px solid red; color: green;', 'incorrect parsing');
assert.equal(dom.serializeStyle(dom.parseStyle('border: 1px solid rgb(0, 255, 255); color: green')), 'border: 1px solid rgb(0, 255, 255); color: green;', 'incorrect parsing');
assert.equal(dom.serializeStyle(
dom.parseStyle('border-top: 1px solid red; border-left: 1px solid red; border-bottom: 1px solid red; border-right: 1px solid red;')
), 'border: 1px solid red;', 'incorrect parsing');
assert.equal(dom.serializeStyle(
dom.parseStyle('border-width: 1pt 1pt 1pt 1pt; border-style: none none none none; border-color: black black black black;')
), 'border: 1pt none black;', 'incorrect parsing');
assert.equal(dom.serializeStyle(
dom.parseStyle('border-width: 1pt 4pt 2pt 3pt; border-style: solid dashed dotted none; border-color: black red green blue;')
), 'border-width: 1pt 4pt 2pt 3pt; border-style: solid dashed dotted none; border-color: black red green blue;', 'incorrect parsing');
assert.equal(dom.serializeStyle(dom.parseStyle('background: transparent url(test.gif);')), `background: transparent url('Xtest.gifY');`, 'incorrect parsing');
assert.equal(dom.serializeStyle(dom.parseStyle('background: transparent url(http://www.site.com/test.gif?a=1&b=2);')), `background: transparent url('Xhttp://www.site.com/test.gif?a=1&b=2Y');`, 'incorrect parsing');
dom.setHTML('test', ' ');
assert.equal(dom.getAttrib('test2', 'style'), 'margin: 1px;', 'incorrect attribute value');
dom.setHTML('test', ' ');
assert.equal(dom.getAttrib('test2', 'style'), `background-image: url('Xtest.gifY');`, 'incorrect attribute value');
// dom.get('test').innerHTML = ' ';
// equal(dom.getAttrib('test2', 'style'), Env.ue && !window.getSelection ?
// 'border: #00ff00 1px solid;' : 'border: 1px solid #00ff00;'); // IE has a separate output
(dom.get('test') as HTMLElement).innerHTML = ' ';
assert.equal(dom.getAttrib('test2', 'style'), `background-image: url('Xhttp://www.site.com/test.gifY');`, 'incorrect attribute value');
DOM.remove('test');
});
it('addClass', () => {
DOM.add(document.body, 'div', { id: 'test' });
getTestElement().className = '';
DOM.addClass('test', 'abc');
assert.equal(getTestElement().className, 'abc', 'incorrect classname');
DOM.addClass('test', '123');
assert.equal(getTestElement().className, 'abc 123', 'incorrect classname');
getTestElement().innerHTML = ' ';
DOM.addClass(DOM.select('span', 'test'), 'abc');
assert.equal((getTestElement('test2')).className, 'abc', 'incorrect classname');
assert.equal((getTestElement('test3')).className, 'abc', 'incorrect classname');
assert.equal((getTestElement('test4')).className, 'abc', 'incorrect classname');
getTestElement().innerHTML = '';
DOM.remove('test');
});
it('removeClass', () => {
DOM.add(document.body, 'div', { id: 'test' });
getTestElement().className = 'abc 123 xyz';
DOM.removeClass('test', '123');
assert.equal(getTestElement().className, 'abc xyz', 'incorrect classname');
getTestElement().innerHTML = (
' '
);
DOM.removeClass(DOM.select('span', 'test'), 'test1');
assert.equal((getTestElement('test2')).className, '', 'incorrect classname');
assert.equal((getTestElement('test3')).className, 'test', 'incorrect classname');
assert.equal((getTestElement('test4')).className, 'test', 'incorrect classname');
getTestElement().innerHTML = ' ';
DOM.removeClass('test2', 'test');
assert.equal(getTestElement().innerHTML, ' ', 'incorrect classname');
DOM.remove('test');
});
it('hasClass', () => {
DOM.add(document.body, 'div', { id: 'test' });
getTestElement().className = 'abc 123 xyz';
assert.isTrue(DOM.hasClass('test', 'abc'), 'incorrect hasClass result');
assert.isTrue(DOM.hasClass('test', '123'), 'incorrect hasClass result');
assert.isTrue(DOM.hasClass('test', 'xyz'), 'incorrect hasClass result');
assert.isFalse(DOM.hasClass('test', 'aaa'), 'incorrect hasClass result');
getTestElement().className = 'abc';
assert.isTrue(DOM.hasClass('test', 'abc'), 'incorrect hasClass result');
getTestElement().className = 'aaa abc';
assert.isTrue(DOM.hasClass('test', 'abc'), 'incorrect hasClass result');
getTestElement().className = 'abc aaa';
assert.isTrue(DOM.hasClass('test', 'abc'), 'incorrect hasClass result');
DOM.remove('test');
});
it('add', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.add('test', 'span', { class: 'abc 123' }, 'content abc ');
let e = getTestElement().getElementsByTagName('span')[0];
assert.equal(e.className, 'abc 123', 'incorrect className');
assert.equal(e.innerHTML.toLowerCase(), 'content abc ', 'incorrect innerHTML');
DOM.remove(e);
DOM.add('test', 'span', { class: 'abc 123' });
e = getTestElement().getElementsByTagName('span')[0];
assert.equal(e.className, 'abc 123', 'incorrect classname');
DOM.remove(e);
DOM.add('test', 'span');
e = getTestElement().getElementsByTagName('span')[0];
assert.equal(e.nodeName, 'SPAN', 'incorrect nodeName');
DOM.remove(e);
getTestElement().innerHTML = ' ';
DOM.add([ 'test2', 'test3', 'test4' ], 'span', { class: 'abc 123' });
assert.equal(DOM.select('span', 'test').length, 6, 'incorrect length');
DOM.remove('test');
});
it('create', () => {
const e = DOM.create('span', { class: 'abc 123' }, 'content abc ');
assert.equal(e.nodeName, 'SPAN', 'incorrect nodeName');
assert.equal(e.className, 'abc 123', 'incorrect className');
assert.equal(e.innerHTML.toLowerCase(), 'content abc ', 'innerHTML was wrong');
});
it('createHTML', () => {
assert.equal(DOM.createHTML('span', {
id: 'id1',
class: 'abc 123'
}, 'content abc '), 'content abc ');
assert.equal(DOM.createHTML('span', { id: 'id1', class: 'abc 123' }), ' ');
assert.equal(DOM.createHTML('span', { id: null, class: undefined } as any), ' ');
assert.equal(DOM.createHTML('span'), ' ');
assert.equal(DOM.createHTML('span', {}, 'content abc '), 'content abc ');
assert.equal(DOM.createHTML('img', {}), ' ');
});
it('uniqueId', () => {
assert.equal(DOM.uniqueId(), 'mce_0');
assert.equal(DOM.uniqueId(), 'mce_1');
assert.equal(DOM.uniqueId(), 'mce_2');
});
it('showHide', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.show('test');
assert.equal(getTestElement().style.display, '');
assert.isFalse(DOM.isHidden('test'));
DOM.hide('test');
assert.equal(getTestElement().style.display, 'none');
assert.isTrue(DOM.isHidden('test'));
// Cleanup
DOM.setAttrib('test', 'style', '');
DOM.remove('test');
});
it('select', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.setHTML('test', '
test 1
test 4
');
assert.equal(DOM.select('div', 'test').length, 4);
assert.isTrue(DOM.select('div', 'test').reverse !== undefined);
DOM.setHTML('test', 'test 1
test 4
');
assert.equal(DOM.select('div.test2', 'test').length, 2);
DOM.remove('test');
});
it('is', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.setHTML('test', 'test 1
');
assert.isTrue(DOM.is(DOM.get('textX'), 'div'));
assert.isTrue(DOM.is(DOM.get('textX'), 'div#textX.test'));
assert.isFalse(DOM.is(DOM.get('textX'), 'div#textX2'));
assert.isFalse(DOM.is(null, 'div#textX2'));
getTestElement().innerHTML = '';
assert.isTrue(DOM.is(DOM.select('span', 'test'), 'span'));
assert.isTrue(DOM.is(DOM.select('#test2', 'test'), '#test2'));
DOM.remove('test');
});
it('encode', () => {
assert.equal(DOM.encode(`abc<>"&'\u00e5\u00e4\u00f6`), 'abc<>"&'\u00e5\u00e4\u00f6');
});
it('setGetAttrib', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.setAttrib('test', 'class', 'test 123');
assert.equal(DOM.getAttrib('test', 'class'), 'test 123');
DOM.setAttrib('test', 'src', 'url');
assert.equal(DOM.getAttrib('test', 'src'), 'url');
assert.equal(DOM.getAttrib('test', 'data-mce-src'), 'url');
assert.equal(DOM.getAttrib('test', 'abc'), '');
DOM.setAttribs('test', { class: '123', title: 'abc' });
assert.equal(DOM.getAttrib('test', 'class'), '123');
assert.equal(DOM.getAttrib('test', 'title'), 'abc');
DOM.setAttribs('test', {});
assert.equal(DOM.getAttrib('test', 'class'), '123');
assert.equal(DOM.getAttrib('test', 'title'), 'abc');
const dom = DOMUtils(document, {
keep_values: true, url_converter: (u, n) => {
return '&<>"' + u + '&<>"' + n;
}
});
dom.setAttribs('test', { src: '123', href: 'abc' });
assert.equal(DOM.getAttrib('test', 'src'), '&<>"123&<>"src');
assert.equal(DOM.getAttrib('test', 'href'), '&<>"abc&<>"href');
const nonElement = document as unknown as Element;
assert.equal(DOM.getAttrib(nonElement, 'test'), '');
assert.equal(DOM.getAttrib(nonElement, 'test', ''), '');
assert.equal(DOM.getAttrib(nonElement, 'test', 'x'), 'x');
DOM.remove('test');
});
it('setGetAttrib on null', () => {
assert.equal(DOM.getAttrib(null, 'test'), '');
DOM.setAttrib(null, 'test', null);
});
it('getAttribs', () => {
const check = (obj: NamedNodeMap | Attr[], val: string) => {
let count = 0;
const values = val.split(',');
Tools.each(obj, (o) => {
if (Tools.inArray(values, o.nodeName.toLowerCase()) !== -1 && o.specified) {
count++;
}
});
return count === obj.length;
};
DOM.add(document.body, 'div', { id: 'test' });
getTestElement().innerHTML = ' ';
assert.isTrue(check(DOM.getAttribs('test2'), 'id,class'));
getTestElement().innerHTML = ' ';
assert.isTrue(check(DOM.getAttribs('test2'), 'id,type,name,value,disabled,readonly,checked'));
DOM.remove('test');
});
it('setGetStyles', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.setStyle('test', 'font-size', '20px');
assert.equal(DOM.getStyle('test', 'font-size'), '20px');
DOM.setStyle('test', 'fontSize', '21px');
assert.equal(DOM.getStyle('test', 'fontSize'), '21px');
DOM.setStyles('test', { fontSize: '22px', display: 'inline' });
assert.equal(DOM.getStyle('test', 'fontSize'), '22px');
assert.equal(DOM.getStyle('test', 'display'), 'inline');
DOM.setStyle('test', 'fontSize', 23);
assert.equal(DOM.getStyle('test', 'fontSize'), '23px');
DOM.setStyle('test', 'fontSize', 23);
DOM.setStyle('test', 'fontSize', '');
assert.equal(DOM.getStyle('test', 'fontSize'), '');
DOM.setStyle('test', 'fontSize', 23);
DOM.setStyle('test', 'fontSize', null);
assert.equal(DOM.getStyle('test', 'fontSize'), '');
DOM.setAttrib('test', 'style', '');
assert.isUndefined(DOM.getStyle(null, 'fontSize'));
DOM.remove('test');
});
it('getPos', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.setStyles('test', { position: 'absolute', left: 100, top: 110 });
assert.equal(Math.round(DOM.getPos('test').x), 100);
assert.equal(Math.round(DOM.getPos('test').y), 110);
DOM.setAttrib('test', 'style', '');
DOM.remove('test');
});
const eqNodeName = (name: string) => (n: Node) => n.nodeName === name;
it('getParent', () => {
DOM.add(document.body, 'div', { id: 'test' });
getTestElement().innerHTML = '';
assert.equal(DOM.getParent('test2', eqNodeName('SPAN'))?.nodeName, 'SPAN');
assert.equal(DOM.getParent('test2', eqNodeName('BODY'))?.nodeName, 'BODY');
assert.isNull(DOM.getParent('test2', eqNodeName('BODY'), document.body));
assert.isNull(DOM.getParent('test2', eqNodeName('X')));
assert.equal(DOM.getParent('test2', 'SPAN')?.nodeName, 'SPAN');
assert.isNull(DOM.getParent('test2', 'body', getTestElement()));
getTestElement().innerHTML = '';
DOM.remove('test');
});
it('getParents', () => {
DOM.add(document.body, 'div', { id: 'test' });
getTestElement().innerHTML = '';
assert.lengthOf(DOM.getParents('test2', eqNodeName('SPAN')), 2);
assert.lengthOf(DOM.getParents('test2', 'span'), 2);
assert.lengthOf(DOM.getParents('test2', 'span.test'), 1);
assert.lengthOf(DOM.getParents('test2', 'body', getTestElement()), 0);
// getParents should stop at DocumentFragment
const frag = document.createDocumentFragment();
const inputElm = document.createElement('input');
frag.appendChild(inputElm);
assert.lengthOf(DOM.getParents(inputElm, (d) => d.nodeName === '#document-fragment'), 0);
DOM.remove('test');
});
it('getViewPort', () => {
const wp = DOM.getViewPort();
assert.equal(wp.x, 0);
assert.equal(wp.y, 0);
assert.isAbove(wp.w, 0);
assert.isAbove(wp.h, 0);
});
it('getRect', () => {
let r;
DOM.add(document.body, 'div', { id: 'test' });
DOM.setStyles('test', { position: 'absolute', left: 100, top: 110, width: 320, height: 240 });
r = DOM.getRect('test');
assert.equal(Math.round(r.x), 100);
assert.equal(Math.round(r.y), 110);
assert.equal(Math.round(r.w), 320);
assert.equal(Math.round(r.h), 240);
DOM.setAttrib('test', 'style', '');
getTestElement().innerHTML = '';
r = DOM.getRect('test2');
assert.equal(r.w, 160);
DOM.remove('test');
});
it('getSize', () => {
let r: { w: number; h: number };
DOM.add(document.body, 'div', { id: 'test' });
getTestElement().innerHTML = '';
r = DOM.getSize('test2');
assert.equal(r.w, 160);
getTestElement().innerHTML = '';
r = DOM.getSize('test2');
assert.equal(r.w, 100);
DOM.remove('test');
});
// TODO: Add test comments
it('getNext', () => {
DOM.add(document.body, 'div', { id: 'test' });
getTestElement().innerHTML = 'A B C ';
assert.equal(DOM.getNext(getTestElement().firstChild as HTMLElement, '*')?.nodeName, 'SPAN');
assert.equal(DOM.getNext(getTestElement().firstChild as HTMLElement, 'em')?.nodeName, 'EM');
assert.isNull(DOM.getNext(getTestElement().firstChild as HTMLElement, 'div'));
assert.isNull(DOM.getNext(null, 'div'));
assert.equal(DOM.getNext(getTestElement().firstChild as HTMLElement, eqNodeName('EM'))?.nodeName, 'EM');
DOM.remove('test');
});
it('getPrev', () => {
DOM.add(document.body, 'div', { id: 'test' });
getTestElement().innerHTML = 'A B C ';
assert.equal(DOM.getPrev(getTestElement().lastChild as HTMLElement, '*')?.nodeName, 'SPAN');
assert.equal(DOM.getPrev(getTestElement().lastChild as HTMLElement, 'strong')?.nodeName, 'STRONG');
assert.isNull(DOM.getPrev(getTestElement().lastChild as HTMLElement, 'div'));
assert.isNull(DOM.getPrev(null, 'div'));
assert.equal(DOM.getPrev(getTestElement().lastChild as HTMLElement, eqNodeName('STRONG'))?.nodeName, 'STRONG');
DOM.remove('test');
});
it('loadCSS', () => {
let c = 0;
DOM.loadCSS('tinymce/dom/test.css?a=1,tinymce/dom/test.css?a=2,tinymce/dom/test.css?a=3');
Tools.each(document.getElementsByTagName('link'), (n) => {
if (n.href.indexOf('test.css?a=') !== -1 && !n.crossOrigin) {
c++;
}
});
assert.equal(c, 3);
});
it('loadCSS contentCssCors enabled', () => {
let c = 0;
// Create an iframe to load in, so that we are using a different document. Otherwise DOMUtils will fallback to using the default.
const iframe = DOM.create('iframe', { src: `javascript=''` });
DOM.add(document.body, iframe);
const iframeDoc = iframe.contentWindow?.document || iframe.contentDocument;
if (Type.isNullable(iframeDoc)) {
assert.fail('Iframe document is not available');
return;
}
iframeDoc.open();
iframeDoc.write('');
iframeDoc.close();
const CustomDOM = DOMUtils(iframeDoc, { keep_values: true, schema: Schema(), contentCssCors: true });
CustomDOM.loadCSS('tinymce/dom/test_cors.css?a=1,tinymce/dom/test_cors.css?a=2,tinymce/dom/test_cors.css?a=3');
Tools.each(iframeDoc.getElementsByTagName('link'), (n) => {
if (n.href.indexOf('test_cors.css?a=') !== -1 && n.crossOrigin === 'anonymous') {
c++;
}
});
DOM.remove(iframe);
assert.equal(c, 3);
});
it('insertAfter', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.setHTML('test', ' ');
DOM.insertAfter(DOM.create('br'), 'test2');
assert.equal(DOM.get('test2')?.nextSibling?.nodeName, 'BR');
DOM.setHTML('test', 'test test ');
DOM.insertAfter(DOM.create('br'), 'test2');
assert.equal(DOM.get('test2')?.nextSibling?.nodeName, 'BR');
DOM.remove('test');
});
it('isBlock', () => {
assert.isTrue(DOM.isBlock(DOM.create('div')));
assert.isTrue(DOM.isBlock('DIV'));
assert.isFalse(DOM.isBlock('SPAN'));
assert.isTrue(DOM.isBlock('div'));
assert.isFalse(DOM.isBlock('a'), 'Anchor name should not be block');
assert.isTrue(DOM.isBlock(DOM.create('a', { 'data-mce-block': 'true' } )), 'Anchor with data-mce-block should be block');
assert.isFalse(DOM.isBlock(DOM.create('a')), 'Anchor without data-mce-block should not be block');
});
it('remove', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.setHTML('test', 'test test2 ');
DOM.remove('test2', true);
assert.equal(getTestElement().innerHTML.toLowerCase(), 'test test2 ');
DOM.setHTML('test', 'test test2 ');
assert.equal((DOM.remove('test2') as Node).nodeName, 'SPAN');
DOM.remove('test');
});
it('replace', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.setHTML('test', 'test test2 ');
DOM.replace(DOM.create('div', { id: 'test2' }), 'test2', true);
assert.equal(DOM.get('test2')?.innerHTML.toLowerCase(), 'test test2 ');
DOM.setHTML('test', 'test test2 ');
DOM.replace(DOM.create('div', { id: 'test2' }), 'test2');
assert.equal(DOM.get('test2')?.innerHTML, '');
DOM.remove('test');
});
it('getOuterHTML', () => {
DOM.add(document.body, 'div', { id: 'test' });
DOM.setHTML('test', 'test test2 ');
assert.equal(DOM.getOuterHTML('test2').toLowerCase().replace(/\"/g, ''), 'test test2 ');
DOM.setHTML('test', 'test test2 ');
DOM.setOuterHTML('test2', '123
');
assert.equal(Tools.trim(DOM.getOuterHTML('test2') || '').toLowerCase().replace(/\"/g, ''), '123
');
DOM.setHTML('test', 'test test2 ');
DOM.setOuterHTML('test2', '123
abc
');
assert.equal(Tools.trim(getTestElement().innerHTML).toLowerCase().replace(/>\s+<').replace(/\"/g, ''), '123
abc
');
DOM.setHTML('test', 'test');
assert.equal(Tools.trim(DOM.getOuterHTML(getTestElement().firstChild as Element)), 'test');
DOM.remove('test');
});
it('encodeDecode', () => {
assert.equal(DOM.encode('\u00e5\u00e4\u00f6&<>"'), '\u00e5\u00e4\u00f6&<>"');
assert.equal(DOM.decode('åäö&<>"'), '\u00e5\u00e4\u00f6&<>"');
});
it('split', () => {
let point: Element;
let parent: Element;
DOM.add(document.body, 'div', { id: 'test' });
DOM.setHTML('test', 'text1inner text2
');
parent = DOM.select('p', getTestElement())[0];
point = DOM.select('span', getTestElement())[0];
DOM.split(parent, point);
assert.equal(getTestElement().innerHTML.toLowerCase().replace(/\s+/g, ''), 'text1
inner text2
');
DOM.setHTML('test', ' cd
');
parent = DOM.select('strong', getTestElement())[0];
point = DOM.select('span', getTestElement())[0];
DOM.split(parent, point);
assert.equal(getTestElement().innerHTML.toLowerCase(), ' cd
', 'TINY-6251: Do not remove spaces containing nbsp');
DOM.setHTML('test', '');
parent = DOM.select('li:nth-child(1)', getTestElement())[0];
point = DOM.select('ul li:nth-child(2)', getTestElement())[0];
DOM.split(parent, point);
assert.equal(HtmlUtils.cleanHtml(getTestElement().innerHTML), '');
DOM.setHTML('test', ' inner text2
');
parent = DOM.select('p', getTestElement())[0];
point = DOM.select('span', getTestElement())[0];
DOM.split(parent, point);
assert.equal(HtmlUtils.cleanHtml(getTestElement().innerHTML), '
inner text2
');
DOM.setHTML('test', 'inner text2
');
parent = DOM.select('p', getTestElement())[0];
point = DOM.select('span', getTestElement())[0];
DOM.split(parent, point);
assert.equal(HtmlUtils.cleanHtml(getTestElement().innerHTML), '
inner text2
');
DOM.setHTML('test', 'text t ext
');
parent = DOM.select('span', getTestElement())[0];
point = DOM.select('span', getTestElement())[1];
DOM.split(parent, point);
assert.equal(getTestElement().innerHTML, 'text t ext
', 'TINY-6268: Do not remove spaces at start of split');
DOM.setHTML('test', 'text text
');
parent = DOM.select('span', getTestElement())[0];
point = DOM.select('span', getTestElement())[1];
DOM.split(parent, point);
assert.equal(getTestElement().innerHTML, 'text text
', 'TINY-6268: Do not remove spaces at end of split');
DOM.remove('test');
});
it('nodeIndex', () => {
DOM.add(document.body, 'div', { id: 'test' }, 'abcabc abc');
assert.equal(DOM.nodeIndex(getTestElement().childNodes[0]), 0);
assert.equal(DOM.nodeIndex(getTestElement().childNodes[1]), 1);
assert.equal(DOM.nodeIndex(getTestElement().childNodes[2]), 2);
getTestElement().insertBefore(DOM.doc.createTextNode('a'), getTestElement().firstChild);
getTestElement().insertBefore(DOM.doc.createTextNode('b'), getTestElement().firstChild);
assert.equal(DOM.nodeIndex(getTestElement().lastChild as Text), 4);
assert.equal(DOM.nodeIndex(getTestElement().lastChild as Text, true), 2);
DOM.remove('test');
});
it('isEmpty without defined schema', () => {
DOM.add(document.body, 'div', { id: 'test' }, '');
const domUtils = DOMUtils(document);
DOM.setHTML('test', ' ');
assert.isFalse(domUtils.isEmpty(getTestElement()));
DOM.setHTML('test', '
');
assert.isTrue(domUtils.isEmpty(getTestElement()));
DOM.remove('test');
});
it('isEmpty', () => {
DOM.schema = Schema(); // A schema will be added when used within a editor instance
DOM.add(document.body, 'div', { id: 'test' }, '');
assert.isTrue(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', ' ');
assert.isTrue(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', ' ');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', 'text');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', 'text ');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', ' ');
assert.isTrue(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', '
');
assert.isTrue(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', 'X
');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', '
');
assert.isTrue(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', '');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', ' ');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', ' ');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', ' ');
assert.isTrue(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', '
');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', ' ');
assert.isTrue(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', 'a ');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', 'a ');
assert.isTrue(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', 'a b');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', ' ');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', ' ');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', '');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.setHTML('test', ' ');
assert.isFalse(DOM.isEmpty(getTestElement()));
DOM.remove('test');
});
it('isEmpty with list of elements considered non-empty', () => {
const elm = DOM.create('p', {}, ' ');
assert.isFalse(DOM.isEmpty(elm, { img: true }));
});
it('isEmpty on pre', () => {
const elm = DOM.create('pre', {}, ' ');
assert.isFalse(DOM.isEmpty(elm));
});
it('isEmpty with list of elements considered non-empty without schema', () => {
const domWithoutSchema = DOMUtils(document, { keep_values: true });
const elm = domWithoutSchema.create('p', {}, ' ');
assert.isFalse(domWithoutSchema.isEmpty(elm, { img: true }));
});
it('isEmpty on P with BR in EM', () => {
const elm = DOM.create('p', {}, ' ');
assert.isTrue(DOM.isEmpty(elm));
});
it('isEmpty on P with two BR in EM', () => {
const elm = DOM.create('p', {}, ' ');
assert.equal(false, DOM.isEmpty(elm));
});
it('bind/unbind/fire', () => {
let count = 0;
DOM.bind(document, 'click', () => {
count++;
});
DOM.dispatch(document, 'click');
DOM.unbind(document, 'click');
assert.equal(count, 1);
count = 0;
DOM.bind([ document, window ], 'click', (e) => {
e.stopPropagation();
count++;
});
DOM.dispatch(document, 'click');
DOM.dispatch(window, 'click');
DOM.unbind([ document, window ], 'click');
assert.equal(count, 2);
count = 0;
DOM.dispatch(document, 'click');
DOM.dispatch(window, 'click');
assert.equal(count, 0);
});
context('get', () => {
it('by id in head', () => {
const DOM = DOMUtils(document, { keep_values: true, schema: Schema() });
const meta: HTMLMetaElement = document.createElement('meta');
meta.setAttribute('id', 'myid');
document.head.appendChild(meta);
assert.strictEqual(DOM.get('myid'), meta, 'get meta');
document.head.removeChild(meta);
});
it('does not find element by name in head', () => {
const DOM = DOMUtils(document, { keep_values: true, schema: Schema() });
const meta: HTMLMetaElement = document.createElement('meta');
meta.setAttribute('name', 'myname');
document.head.appendChild(meta);
assert.isNull(DOM.get('myname'), 'get meta');
document.head.removeChild(meta);
});
it('does not find element by name in body', () => {
const DOM = DOMUtils(document, { keep_values: true, schema: Schema() });
const meta: HTMLMetaElement = document.createElement('meta');
meta.setAttribute('name', 'myname');
document.body.appendChild(meta);
assert.isNull(DOM.get('myname'), 'get meta');
document.body.removeChild(meta);
});
it('finds element by id in body, not element by name in head', () => {
const DOM = DOMUtils(document, { keep_values: true, schema: Schema() });
const meta = document.createElement('meta');
meta.setAttribute('name', 'myname');
document.head.appendChild(meta);
const div = document.createElement('div');
div.setAttribute('id', 'myname');
document.body.appendChild(div);
assert.strictEqual(DOM.get('myname'), div, 'get div');
document.head.removeChild(meta);
document.body.removeChild(div);
});
it('returns element', () => {
const DOM = DOMUtils(document, { keep_values: true, schema: Schema() });
const e = document.createElement('div');
assert.strictEqual(DOM.get(e), e, 'get');
});
});
context('isChildOf', () => {
it('same node', () => {
const p = document.createElement('div');
assert.isTrue(DOM.isChildOf(p, p), 'Same node');
});
it('child nodes', () => {
const p = document.createElement('div');
const m = document.createElement('p');
const s = document.createTextNode('Content');
m.appendChild(s);
assert.isFalse(DOM.isChildOf(s, p), 'Detached text node');
assert.isFalse(DOM.isChildOf(m, p), 'Detached para node');
p.appendChild(m);
assert.isTrue(DOM.isChildOf(s, p), 'Attached text node');
assert.isTrue(DOM.isChildOf(m, p), 'Attached para node');
});
});
context('isEditable', () => {
const withScratchDomUtils = (innerHtml: string, f: (dom: DOMUtils, root: SugarElement) => void) => {
const root = SugarElement.fromHtml(`${innerHtml}
`);
Insert.append(SugarBody.body(), root);
const dom = DOMUtils(document, { root_element: root.dom, schema: Schema() });
f(dom, root);
dom.destroy();
Remove.remove(root);
};
const testIsEditable = (testCase: { input: string; editableRoot?: boolean; path: number[]; expected: boolean }) => () => {
withScratchDomUtils(testCase.input, (domUtils, root) => {
const scope = Hierarchy.follow(root, testCase.path).getOrDie('Could not find element by path');
ContentEditable.set(root, testCase.editableRoot ?? true);
assert.equal(domUtils.isEditable(scope.dom), testCase.expected, 'Should be the expected editable state');
});
};
it('TINY-9462: isEditable on text node in editable root', testIsEditable({
input: 'text node',
path: [],
expected: true
}));
it('TINY-9462: isEditable on text node in noneditable root', testIsEditable({
input: 'text node',
editableRoot: false,
path: [],
expected: false
}));
it('TINY-9462: isEditable on element in editable root', testIsEditable({
input: 'test
',
path: [ 0 ],
expected: true
}));
it('TINY-9462: isEditable on element in noneditable root', testIsEditable({
input: 'test
',
editableRoot: false,
path: [ 0 ],
expected: false
}));
it('TINY-9462: isEditable on text node in element in editable root', testIsEditable({
input: 'test
',
path: [ 0, 0 ],
expected: true
}));
it('TINY-9462: isEditable on text node in element in noneditable root', testIsEditable({
input: 'test
',
editableRoot: false,
path: [ 0, 0 ],
expected: false
}));
it('TINY-9462: isEditable on text node in noneditable element in editable root', testIsEditable({
input: 'test
',
path: [ 0, 0 ],
expected: false
}));
it('TINY-9462: isEditable on text node in editable element in noneditable root', testIsEditable({
input: 'test
',
editableRoot: false,
path: [ 0, 0 ],
expected: true
}));
it('TINY-9462: isEditable on element in noneditable element in editable root', testIsEditable({
input: '
',
path: [ 0, 0 ],
expected: false
}));
it('TINY-9462: isEditable on element in editable element in noneditable root', testIsEditable({
input: '
',
editableRoot: false,
path: [ 0, 0 ],
expected: true
}));
});
});