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 2
test 3
test 4
'); assert.equal(DOM.select('div', 'test').length, 4); assert.isTrue(DOM.select('div', 'test').reverse !== undefined); DOM.setHTML('test', '
test 1
test 2
test 3
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 = '
ababcc
'; 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 = '
ababcc
'; 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 = '
ababcc
'; 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 = 'ABC'; 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 = 'ABC'; 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', 'testtest'); 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', 'testtest2'); DOM.remove('test2', true); assert.equal(getTestElement().innerHTML.toLowerCase(), 'testtest2'); DOM.setHTML('test', 'testtest2'); assert.equal((DOM.remove('test2') as Node).nodeName, 'SPAN'); DOM.remove('test'); }); it('replace', () => { DOM.add(document.body, 'div', { id: 'test' }); DOM.setHTML('test', 'testtest2'); DOM.replace(DOM.create('div', { id: 'test2' }), 'test2', true); assert.equal(DOM.get('test2')?.innerHTML.toLowerCase(), 'testtest2'); DOM.setHTML('test', 'testtest2'); 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', 'testtest2'); assert.equal(DOM.getOuterHTML('test2').toLowerCase().replace(/\"/g, ''), 'testtest2'); DOM.setHTML('test', 'testtest2'); DOM.setOuterHTML('test2', '
123
'); assert.equal(Tools.trim(DOM.getOuterHTML('test2') || '').toLowerCase().replace(/\"/g, ''), '
123
'); DOM.setHTML('test', 'testtest2'); 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', '

text1innertext2

'); 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', '

  innertext2

'); 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', '

innertext2

'); 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 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 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' }, 'abcabcabc'); 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', 'ab'); 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 })); }); });