diff --git a/server.js b/server.js index d94c4bdf6..efaca877f 100644 --- a/server.js +++ b/server.js @@ -1,35 +1,35 @@ -var webpack = require('webpack') -var webpackDevMiddleware = require('webpack-dev-middleware') -var webpackHotMiddleware = require('webpack-hot-middleware') -var config = require('./webpack.config') -var compression = require('compression') -var app = new (require('express'))() +const webpack = require('webpack'); +const webpackDevMiddleware = require('webpack-dev-middleware'); +const webpackHotMiddleware = require('webpack-hot-middleware'); +const config = require('./webpack.config'); +const compression = require('compression'); +const app = new (require('express'))(); -app.use(compression()) +app.use(compression()); -var port = 2000 +const port = 2000; -var compiler = webpack(config) +const compiler = webpack(config); app.use(webpackDevMiddleware(compiler, { stats: { colors: true }, noInfo: true, publicPath: config.output.publicPath -})) +})); -app.use(webpackHotMiddleware(compiler)) +app.use(webpackHotMiddleware(compiler)); app.get("/", function(req, res) { res.sendFile(__dirname + '/index.html') -}) +}); app.get("/test.html", function(req, res) { res.sendFile(__dirname + '/test.html') -}) +}); -app.get("/dist/jodit.*", function(req, res) { +app.get("/build/jodit.*", function(req, res) { res.sendFile(__dirname + '/' + req.url) -}) +}); app.use('/node_modules', require('express').static(__dirname + '/node_modules')); diff --git a/src/Config.ts b/src/Config.ts index dca14a2b7..1cb9da907 100644 --- a/src/Config.ts +++ b/src/Config.ts @@ -599,21 +599,21 @@ Config.prototype.controls = { editor.i18n('License: %s', !isLicense(editor.options.license) ? editor.i18n('Free Non-commercial Version') : normalizeLicense(editor.options.license)) + '' + '
' + - 'http://xdsoft.net/jodit/' + + 'http://xdsoft.net/jodit/' + '
' + '
' + - '' + editor.i18n('Jodit User\'s Guide') + ' ' + + '' + editor.i18n('Jodit User\'s Guide') + ' ' + editor.i18n('contains detailed help for using') + '
' + '
' + editor.i18n('For information about the license, please go to our website:') + '
' + '
' + - 'http://xdsoft.net/jodit/license.html' + + 'http://xdsoft.net/jodit/license.html' + '
' + (isLicense(editor.options.license) ? '' : '
' + - '' + editor.i18n('Buy full version') + '' + + '' + editor.i18n('Buy full version') + '' + '
' ) + '
' + @@ -637,7 +637,7 @@ Config.prototype.controls = { editor.selection.insertNode(dom('', editor.editorDocument)); }; - let sourceImage: HTMLImageElement|null = null; + let sourceImage: HTMLImageElement | null = null; if (current && current.nodeType !== Node.TEXT_NODE && (current.tagName === 'IMG' || $$('img', current).length)) { sourceImage = current.tagName === 'IMG' ? current : $$('img', current)[0]; diff --git a/src/Jodit.ts b/src/Jodit.ts index 00752777e..679133db7 100644 --- a/src/Jodit.ts +++ b/src/Jodit.ts @@ -390,10 +390,16 @@ export class Jodit extends Component { } } + isDestructed: boolean = false; /** * Jodit's Destructor. Remove editor, and return source input */ destruct() { + if (this.isDestructed) { + return; + } + + this.isDestructed = true; /** * Triggered before {@link events:beforeDestruct|beforeDestruct} executed. If returned false method stopped * diff --git a/src/constants.ts b/src/constants.ts index 9fcf32fd7..643dab56e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -14,6 +14,7 @@ export const SPACE_REG_EXP_START = /^[\s\n\t\r\uFEFF\u200b]+/g; export const SPACE_REG_EXP_END = /[\s\n\t\r\uFEFF\u200b]+$/g; export const IS_BLOCK = /^(PRE|DIV|P|LI|H[1-6]|BLOCKQUOTE|TD|TH|TABLE|BODY|HTML|FIGCAPTION|FIGURE)$/i; +export const IS_SPAN = /^(STRONG|SPAN|I|EM|B)$/; export const KEY_BACKSPACE = 8; export const KEY_TAB = 9; diff --git a/src/modules/Dom.ts b/src/modules/Dom.ts index b7f318851..2dea98b05 100644 --- a/src/modules/Dom.ts +++ b/src/modules/Dom.ts @@ -22,13 +22,13 @@ export class Dom { /** * * @param {Node} current - * @param {String|Node} tag + * @param {String | Node} tag * @param {Jodit} editor * * @return {HTMLElement} */ - static wrap = (current: Node, tag: Node|string, editor: Jodit): HTMLElement => { - let tmp: false|Node|HTMLElement|HTMLTableCellElement, + static wrapInline = (current: Node, tag: Node|string, editor: Jodit): HTMLElement => { + let tmp: null | Node, first: Node = current, last: Node = current; @@ -37,7 +37,7 @@ export class Dom { let needFindNext: boolean = false; do { needFindNext = false; - tmp = Dom.prev(first, (elm) => !!elm, editor.editor, false); + tmp = first.previousSibling; if (tmp && !Dom.isBlock(tmp)) { needFindNext = true; first = tmp; @@ -46,7 +46,7 @@ export class Dom { do { needFindNext = false; - tmp = Dom.next(last, (elm) => !!elm, editor.editor, false); + tmp = last.nextSibling; if (tmp && !Dom.isBlock(tmp)) { needFindNext = true; last = tmp; @@ -60,7 +60,7 @@ export class Dom { first.parentNode.insertBefore(wrapper, first); } - let next: Node|null = first; + let next: Node | null = first; while (next) { next = first.nextSibling; @@ -72,6 +72,32 @@ export class Dom { } + editor.selection.restore(selInfo); + + return wrapper; + }; + /** + * + * @param {Node} current + * @param {String | Node} tag + * @param {Jodit} editor + * + * @return {HTMLElement} + */ + static wrap = (current: Node, tag: Node|string, editor: Jodit): HTMLElement | null => { + + const selInfo = editor.selection.save(); + + const wrapper = typeof tag === 'string' ? Dom.create(tag, '', editor.editorDocument) : tag; + + if (!current.parentNode) { + return null; + } + + current.parentNode.insertBefore(wrapper, current); + + wrapper.appendChild(current); + editor.selection.restore(selInfo); return wrapper; @@ -82,7 +108,7 @@ export class Dom { * @param node */ static unwrap(node: Node) { - let parent: Node|null = node.parentNode, + let parent: Node | null = node.parentNode, el = node; if (parent) { @@ -108,7 +134,7 @@ export class Dom { * ``` */ static each (elm: Node|HTMLElement, callback: (this: Node, node: Node) => void|false): boolean { - let node: Node|null|false = elm.firstChild; + let node: Node | null | false = elm.firstChild; if (node) { while (node) { @@ -375,6 +401,8 @@ export class Dom { } static isEmpty(node: Node): boolean { + const isNoEmtyElement: RegExp = /^(img|svg|canvas|input|textarea|form)$/; + if (!node) { return true; } @@ -383,7 +411,7 @@ export class Dom { return node.nodeValue === null || trim(node.nodeValue).length === 0; } - return Dom.each(node, (elm: Node | null): false | void => { + return !node.nodeName.toLowerCase().match(isNoEmtyElement) && Dom.each(node, (elm: Node | null): false | void => { if ( ( elm && elm.nodeType === Node.TEXT_NODE && @@ -394,7 +422,7 @@ export class Dom { ) || ( elm && elm.nodeType === Node.ELEMENT_NODE && - elm.nodeName.match(/^(img|table)$/i) + elm.nodeName.toLowerCase().match(isNoEmtyElement) ) ) { return false; diff --git a/src/modules/Helpers.ts b/src/modules/Helpers.ts index 951b7bd9d..64748802f 100644 --- a/src/modules/Helpers.ts +++ b/src/modules/Helpers.ts @@ -659,7 +659,7 @@ export const debounce = function (this: any, fn: Function, timeout ?: number, i if (timeout) { clearTimeout(timer); - timer = window.setTimeout(function () { + timer = window.setTimeout(() => { if (!invokeAsap) { fn.apply(ctx, args); } diff --git a/src/modules/Selection.ts b/src/modules/Selection.ts index ab06a024d..e4db8d656 100644 --- a/src/modules/Selection.ts +++ b/src/modules/Selection.ts @@ -10,6 +10,7 @@ import {each, dom, trim, $$, css, normilizeCSSValue, isIE, isPlainObject, normal import {Dom} from "./Dom"; import {Jodit} from "../Jodit"; import {INVISIBLE_SPACE_REG_EXP_END, INVISIBLE_SPACE_REG_EXP_START} from "../constants"; +import {INVISIBLE_SPACE} from "../constants"; export type markerInfo = { startId: string, @@ -563,9 +564,25 @@ export class Select extends Component{ return node === end; }, this.jodit.editor, true, 'nextSibling', false); - nodes.forEach((current: Node) => { + let forEvery = (current: Node) => { + if (current.nodeName.match(/^(UL|OL)$/)) { + return [].slice.call(current.childNodes).forEach(forEvery); + } + + if (current.nodeName === 'LI') { + if (current.firstChild) { + current = current.firstChild; + } else { + let currentB: Node = this.jodit.editorDocument.createTextNode(INVISIBLE_SPACE); + current.appendChild(currentB); + current = currentB; + } + } + callback(current); - }) + }; + + nodes.forEach(forEvery) } }; @@ -868,7 +885,7 @@ export class Select extends Component{ leftRange.setStartBefore(wrapper); leftRange.setEndBefore(font); - const leftFragment = leftRange.extractContents(); + const leftFragment: DocumentFragment = leftRange.extractContents(); if ((!leftFragment.textContent || !trim(leftFragment.textContent).length) && leftFragment.firstChild) { Dom.unwrap(leftFragment.firstChild); @@ -944,7 +961,7 @@ export class Select extends Component{ if (nodeName.toUpperCase() === defaultTag || !clearStyle) { const node: Node = Dom.create(nodeName, consts.INVISIBLE_SPACE, this.jodit.editorDocument); - this.insertNode(node); + this.insertNode(node, false, false); if (nodeName.toUpperCase() === defaultTag && cssRules) { css(node, cssRules); } diff --git a/src/plugins/cleanHTML.ts b/src/plugins/cleanHTML.ts index b890f4581..2416ebed1 100644 --- a/src/plugins/cleanHTML.ts +++ b/src/plugins/cleanHTML.ts @@ -7,9 +7,10 @@ import {Jodit} from '../Jodit'; import {Config} from '../Config' import * as consts from '../constants'; -import {cleanFromWord, normalizeNode, trim} from "../modules/Helpers"; +import {cleanFromWord, debounce, normalizeNode, trim} from "../modules/Helpers"; import {Dom} from "../modules/Dom"; import {Select} from "../modules/Selection"; +import {IS_SPAN} from "../constants"; /** * @property {object} cleanHTML {@link cleanHTML|cleanHTML}'s options @@ -29,8 +30,8 @@ import {Select} from "../modules/Selection"; * var editor = Jodit('#editor', { * allowTags: 'p,a[href],table,tr,td, img[src=1.png]' // allow only

,,,,' + '
, tags and for allow only `href` attribute and allow only `src` atrribute == '1.png' * }); - * editor.setEditorValue('Sorry! Goodby mr. Freeman'); - * console.log(editor.getEditorValue()); //Sorry! Freeman + * editor.value = 'Sorry! Goodby mr. Freeman'; + * console.log(editor.value); //Sorry! Freeman * ``` * * @example @@ -54,17 +55,23 @@ import {Select} from "../modules/Selection"; declare module "../Config" { interface Config { cleanHTML: { + timeout: number; replaceNBSP: boolean, cleanOnPaste: boolean, - allowTags: false|string|{[key:string]: string} + removeEmptyElements: boolean, + allowTags: false | string | {[key:string]: string}, + denyTags: false | string | {[key:string]: string} } } } Config.prototype.cleanHTML = { + timeout: 0, + removeEmptyElements: true, replaceNBSP: true, cleanOnPaste: true, - allowTags: false + allowTags: false, + denyTags: false, }; Config.prototype.controls.eraser = { @@ -77,27 +84,26 @@ Config.prototype.controls.eraser = { * Clean HTML after removeFormat and insertHorizontalRule command */ export function cleanHTML(editor: Jodit) { - if (editor.options.cleanHTML.cleanOnPaste) { editor.events.on('processPaste', (event: Event, html: string) => { return cleanFromWord(html); }); } - if (editor.options.cleanHTML.allowTags) { - const - attributesReg = /([^\[]*)\[([^\]]+)]/, - seperator = /[\s]*,[\s]*/, - attrReg = /^(.*)[\s]*=[\s]*(.*)$/; - let - allowTagsHash: {[key: string]: any} = {}; + const + attributesReg = /([^\[]*)\[([^\]]+)]/, + seperator = /[\s]*,[\s]*/, + attrReg = /^(.*)[\s]*=[\s]*(.*)$/; + + const getHash = (tags: false | string | {[key:string]: string}): {[key: string]: any} | false=> { + const tagsHash: {[key: string]: any} = {}; - if (typeof editor.options.cleanHTML.allowTags === 'string') { - editor.options.cleanHTML.allowTags.split(seperator).map((elm) => { + if (typeof tags === 'string') { + tags.split(seperator).map((elm) => { elm = trim(elm); - let attr = attributesReg.exec(elm), + let attr: RegExpExecArray | null = attributesReg.exec(elm), allowAttributes: {[key: string]: string | boolean} = {}, - attributeMap = function (attr: string) { + attributeMap = (attr: string) => { attr = trim(attr); const val: Array | null = attrReg.exec(attr); if (val) { @@ -108,75 +114,121 @@ export function cleanHTML(editor: Jodit) { }; if (attr) { - let attr2 = attr[2].split(seperator); + const attr2: string[] = attr[2].split(seperator); if (attr[1]) { attr2.map(attributeMap); - allowTagsHash[attr[1].toUpperCase()] = allowAttributes; + tagsHash[attr[1].toUpperCase()] = allowAttributes; } } else { - allowTagsHash[elm.toUpperCase()] = true; + tagsHash[elm.toUpperCase()] = true; } }); - } else { - allowTagsHash = editor.options.cleanHTML.allowTags; + + return tagsHash; } - editor.events.on('beforeSetElementValue', (data: {value: string}) => { - if (editor.getRealMode() === consts.MODE_WYSIWYG) { - const div: HTMLElement = Dom.create('div', '', editor.editorDocument); + if (tags) { + Object.keys(tags).forEach((tagName: string) => { + tagsHash[tagName.toUpperCase()] = tags[tagName]; + }); + return tagsHash; + } - let node: Element|null = null, - remove: Element[] = [], - removeAttrs: string[], - i: number = 0; + return false; + }; - div.innerHTML = data.value; + let + current: Node | false, + allowTagsHash: {[key: string]: any} | false = getHash(editor.options.cleanHTML.allowTags), + denyTagsHash: {[key: string]: any} | false = getHash(editor.options.cleanHTML.denyTags); - if (div.firstChild) { - node = div.firstChild; - } + const isRemovableNode = (node: Node): boolean => { + if ( + node.nodeType !== Node.TEXT_NODE && + ( + (allowTagsHash && !allowTagsHash[node.nodeName]) || + (denyTagsHash && denyTagsHash[node.nodeName]) + ) + ) { + return true; + } - while (node) { - if (node && node.nodeName) { - if (!allowTagsHash[node.nodeName]) { - remove.push(node); - } else if (allowTagsHash[node.nodeName] !== true) { - if (node.attributes && node.attributes.length) { - removeAttrs = []; - for (i = 0; i < node.attributes.length; i += 1) { - if (!allowTagsHash[node.nodeName][node.attributes[i].name] || - ( - allowTagsHash[node.nodeName][node.attributes[i].name] !== true && - allowTagsHash[node.nodeName][node.attributes[i].name] !== node.attributes[i].value - ) - ) { - removeAttrs.push(node.attributes[i].name); - } - } - for (i = 0; i <= removeAttrs.length; i += 1) { - node.removeAttribute(removeAttrs[i]); + if ( + editor.options.cleanHTML.removeEmptyElements && + current && + node.nodeType === Node.ELEMENT_NODE && + node.nodeName.match(IS_SPAN) && + trim((node).innerHTML).length === 0 && + !Dom.isOrContains(node, current) + ) { + return true; + } + + return false; + }; + + + editor.events + .on('change afterSetMode afterInit mousedown keydown', debounce(() => { + if (!editor.isDestructed && editor.isEditorMode()) { + current = editor.selection.current(); + + let node: Node | null = null, + remove: Node[] = [], + work: boolean = false, + i: number = 0; + + const checkNode = (node: Node | null): void => { + if (node) { + if (isRemovableNode(node)) { + remove.push(node); + return checkNode(node.nextSibling); + } + + if (allowTagsHash && allowTagsHash[node.nodeName] !== true) { + if (node.attributes && node.attributes.length) { + const removeAttrs: string[] = []; + for (i = 0; i < node.attributes.length; i += 1) { + if (!allowTagsHash[node.nodeName][node.attributes[i].name] || + ( + allowTagsHash[node.nodeName][node.attributes[i].name] !== true && + allowTagsHash[node.nodeName][node.attributes[i].name] !== node.attributes[i].value + ) + ) { + removeAttrs.push(node.attributes[i].name); } } - } - } - node = Dom.next(node, elm => !!elm, div, true); - } - let parent: Node|null; + if (removeAttrs.length) { + work = true; + } - for (i = 0; i < remove.length; i += 1) { - parent = remove[i].parentNode; - if (remove[i] && parent) { - parent.removeChild(remove[i]); + removeAttrs.forEach((attr: string) => { + (node).removeAttribute(attr); + }); + } } + + checkNode(node.firstChild); + checkNode(node.nextSibling); } + }; - data.value = div.innerHTML; + if (editor.editor.firstChild) { + node = editor.editor.firstChild; } - }); - } - editor.events + + checkNode(node); + + + remove.forEach((node: Node) => node.parentNode && node.parentNode.removeChild(node)); + + if (remove.length || work) { + editor.setEditorValue(); + } + } + }, editor.options.cleanHTML.timeout)) // remove invisible spaces then they already not need .on('keyup', () => { if (editor.selection.isCollapsed()) { diff --git a/src/plugins/enter.ts b/src/plugins/enter.ts index 083b32acd..3aa423fae 100644 --- a/src/plugins/enter.ts +++ b/src/plugins/enter.ts @@ -119,12 +119,14 @@ export function enter(editor: Jodit) { if (!currentBox && current && !Dom.prev(current, (elm: Node | null) => (Dom.isBlock(elm) || (!!elm && Dom.isImage(elm, editor.ownerWindow))), editor.editor)) { let needWrap: Node = current; + Dom.up(needWrap, (node: Node) => { if (node && node.hasChildNodes() && node !== editor.editor) { needWrap = node; } }, editor.editor); - currentBox = Dom.wrap(needWrap, editor.options.enter, editor); + + currentBox = Dom.wrapInline(needWrap, editor.options.enter, editor); range = sel.rangeCount ? sel.getRangeAt(0) : editor.editorDocument.createRange(); } diff --git a/src/plugins/formatBlock.ts b/src/plugins/formatBlock.ts index cb1e2a17f..e778f80ac 100644 --- a/src/plugins/formatBlock.ts +++ b/src/plugins/formatBlock.ts @@ -10,6 +10,7 @@ import * as consts from '../constants'; import {Config} from "../Config"; import {ToolbarButton, ControlType} from "../modules/ToolbarCollection"; import {markerInfo} from "../modules/Selection"; +import {INVISIBLE_SPACE} from "../constants"; Config.prototype.controls.paragraph = { @@ -93,8 +94,8 @@ export function formatBlock(editor: Jodit) { const selectionInfo: markerInfo[] = editor.selection.save(); let currentBox: HTMLElement | false = current ? Dom.up(current, Dom.isBlock, editor.editor) : false; - if (!currentBox && current) { - currentBox = Dom.wrap(current, editor.options.enter, editor); + if ((!currentBox || currentBox.nodeName === 'LI') && current) { + currentBox = Dom.wrapInline(current, editor.options.enter, editor); } if (!currentBox) { @@ -103,12 +104,20 @@ export function formatBlock(editor: Jodit) { } if (!currentBox.tagName.match(/TD|TH|TBODY|TABLE|THEAD/i)) { - Dom.replace(currentBox, third, true, false, editor.editorDocument); + if ( + third === editor.options.enter.toLowerCase() && + currentBox.parentNode && + currentBox.parentNode.nodeName === 'LI' + ) { + Dom.unwrap(currentBox); + } else { + Dom.replace(currentBox, third, true, false, editor.editorDocument); + } } else { if (!editor.selection.isCollapsed()) { editor.selection.applyCSS({}, third) } else { - Dom.wrap(current, third, editor); + Dom.wrapInline(current, third, editor); } } diff --git a/src/plugins/indent.ts b/src/plugins/indent.ts index b6af28aab..f4fa5d1ed 100644 --- a/src/plugins/indent.ts +++ b/src/plugins/indent.ts @@ -51,7 +51,7 @@ export function indent(editor: Jodit) { let currentBox: HTMLElement|false = current ? Dom.up(current, Dom.isBlock, editor.editor) : false; if (!currentBox && current) { - currentBox = Dom.wrap(current, editor.options.enter, editor); + currentBox = Dom.wrapInline(current, editor.options.enter, editor); } if (!currentBox) { diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 63732988f..b81a39231 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -4,8 +4,6 @@ * Copyright 2013-2018 Valeriy Chupurnov https://xdsoft.net */ -import {xpath} from "./xpath"; - export {addNewLine} from "./addNewLine"; export {autofocus} from "./autofocus"; export {backspace} from "./backspace"; @@ -13,7 +11,6 @@ export {bold} from "./bold"; export {cleanHTML} from "./cleanHTML"; export {color} from "./color"; import "./copyformat"; -import {DragAndDrop} from "./DragAndDrop"; export {enter} from "./enter"; export {errorMessages} from "./errorMessages"; export {font} from "./font"; diff --git a/src/plugins/justify.ts b/src/plugins/justify.ts index 1aa194a26..f38537c82 100644 --- a/src/plugins/justify.ts +++ b/src/plugins/justify.ts @@ -130,7 +130,7 @@ export function justify(editor: Jodit) { if (!currentBox && current) { - currentBox = Dom.wrap(current, editor.options.enter, editor); + currentBox = Dom.wrapInline(current, editor.options.enter, editor); } justify(currentBox); diff --git a/src/plugins/resizer.ts b/src/plugins/resizer.ts index 582e1ee62..c6456efd5 100644 --- a/src/plugins/resizer.ts +++ b/src/plugins/resizer.ts @@ -224,6 +224,7 @@ export function resizer(editor: Jodit) { start_x = e.clientX; start_y = e.clientY; + editor.events.fire('hidePopup'); editor.lock(LOCK_KEY); }); }); diff --git a/test/bootstrap.js b/test/bootstrap.js index ffc2b7267..5044cf8fa 100644 --- a/test/bootstrap.js +++ b/test/bootstrap.js @@ -295,3 +295,4 @@ function offset(el) { }); })(); + diff --git a/test/tests/commandsTest.js b/test/tests/commandsTest.js index 3aaf7bdc3..7511bf477 100644 --- a/test/tests/commandsTest.js +++ b/test/tests/commandsTest.js @@ -16,6 +16,20 @@ describe('Commands Jodit Editor Tests', function() { expect(editor.getEditorValue()).to.equal('

test

test2

'); }); + describe('Exec formatBlock for one inline element', function () { + it('Should wrap this element and all nearest inine element in block', function () { + var jodit = new Jodit(appendTestArea()); + jodit.value = 'stop post ice'; + var range = jodit.editorDocument.createRange(); + range.setStart(jodit.editor.firstChild, 0); + range.setEnd(jodit.editor.firstChild, 2); + jodit.selection.selectRange(range); + + jodit.execCommand('formatBlock', false, 'h1'); + + expect(jodit.value).to.equal('

stop post ice

'); + }); + }); it('Try exec the command "formatBlock" in text node then selection is collapsed it should wrap it node in H1', function() { var editor = new Jodit(appendTestArea()); @@ -108,7 +122,7 @@ describe('Commands Jodit Editor Tests', function() { '
  • 2

  • ' + '
  • 3

  • ' + '' - ) + ); editor.execCommand('formatBlock', false, 'p'); expect(editor.value).to.be.equal('
      ' + @@ -384,8 +398,13 @@ describe('Commands Jodit Editor Tests', function() { }); }); describe('Exec bold for collapsed range and move cursor in another place', function () { - it('Should remove stron element', function () { - var editor = new Jodit(appendTestArea()); + it('Should remove STRONG element', function () { + var editor = new Jodit(appendTestArea(), { + cleanHTML: { + timeout: 0 + } + }); + editor.value = 'testtest'; var range = editor.editorDocument.createRange(); range.setStart(editor.editor.firstChild, 4); @@ -396,7 +415,7 @@ describe('Commands Jodit Editor Tests', function() { expect(editor.value).to.be.equal('testtest'); range.setStart(editor.editor.lastChild, 2); - range.collapse(true) + range.collapse(true); editor.selection.selectRange(range); simulateEvent('mousedown', 0, editor.editor) expect(editor.value).to.be.equal('testtest'); diff --git a/test/tests/editorTest.js b/test/tests/editorTest.js index bebd75f65..7c5cc5ef4 100644 --- a/test/tests/editorTest.js +++ b/test/tests/editorTest.js @@ -577,9 +577,13 @@ describe('Jodit Editor Tests', function() { editor.destruct(); }); describe('Cursor position', function () { - it('Set cursor after node', function () { + it('Should set cursor after node', function () { var area = appendTestArea(); - var editor = new Jodit(area); + var editor = new Jodit(area, { + cleanHTML: { + removeEmptyElements: false, + } + }); var spans = [editor.editorDocument.createElement('span'), editor.editorDocument.createElement('span'), editor.editorDocument.createElement('span')]; editor.selection.insertNode(spans[0]); diff --git a/test/tests/enterTest.js b/test/tests/enterTest.js index 6dcccdd9c..946d02808 100644 --- a/test/tests/enterTest.js +++ b/test/tests/enterTest.js @@ -111,6 +111,7 @@ describe('Enter behavior Jodit Editor Tests', function() { '
    ').to.be.equal(editor.getEditorValue()); + editor.selection.focus(); editor.selection.insertNode(editor.editorDocument.createTextNode(' 2 ')); expect('' + diff --git a/test/tests/imageTest.js b/test/tests/imageTest.js index 5a4626934..fe8eb08a2 100644 --- a/test/tests/imageTest.js +++ b/test/tests/imageTest.js @@ -117,39 +117,41 @@ describe('Test image', function() { div.innerHTML = '
    \n' + ' wrong image selection\n' + '
    \n' + - ' \n' + + ' \n' + '
    \n' + '
    '; document.body.appendChild(div); - var editor = new Jodit('#text_area1'); - simulateEvent('mousedown', 0, editor.editor.querySelector('img')); - - var popup = document.querySelector('.jodit_toolbar_popup-inline[data-editor_id=text_area1]'); + var editor = new Jodit(document.getElementById('text_area1')); + simulateEvent('mousedown', 0, editor.editor.querySelector('img')); + // + var popup = editor.ownerDocument.querySelector('.jodit_toolbar_popup-inline[data-editor_id=text_area1]'); + // expect(popup.parentNode.parentNode !== null).to.equal(true); - - var resizer = document.querySelector('.jodit_resizer[data-editor_id=text_area1]'); + // + var resizer = editor.ownerDocument.querySelector('.jodit_resizer[data-editor_id=text_area1]'); expect(resizer.style.display === 'block').to.equal(true); - + // var positionResizer = offset(resizer); - var positionImg = offset(editor.editor.querySelector('img')); - + // simulateEvent('mousedown', 0, resizer.getElementsByTagName('i')[0]); simulateEvent('mousemove', 0, editor.ownerWindow, function (data) { data.clientX = positionResizer.left - 10; data.clientY = positionResizer.top - 10; }); + // + expect(popup.parentNode).to.be.equal(null); - expect(popup.parentNode.parentNode).to.be.equal(null); simulateEvent('mouseup', 0, editor.ownerWindow, function (data) { data.clientX = positionResizer.left - 10; data.clientY = positionResizer.top - 10; }); - expect(popup.parentNode.parentNode).to.be.not.equal(null); + expect(popup.parentNode).to.be.equal(null); + // editor.destruct(); - document.body.removeChild(div); + div.parentNode && div.parentNode.removeChild(div); }); }); }); diff --git a/test/tests/interfaceTest.js b/test/tests/interfaceTest.js index fdbbaca6e..d853b857b 100644 --- a/test/tests/interfaceTest.js +++ b/test/tests/interfaceTest.js @@ -92,9 +92,9 @@ describe('Test interface', function() { simulateEvent('mousedown', 0, editor.container.querySelector('.jodit_toolbar_btn-video')) - var popup = editor.ownerDocument.querySelector('.jodit_toolbar_popup'); + var popup = editor.ownerDocument.querySelector('.jodit_toolbar_popup[data-editor_id=' + editor.id + ']'); - expect(popup && popup.style.display === 'block').to.be.equal(true); + expect(popup).to.be.not.equal(null); var positionPopup = offset(popup); var positionContainer = offset(editor.container); @@ -122,15 +122,14 @@ describe('Test interface', function() { simulateEvent('mousedown', 0, editor.container.querySelector('.jodit_toolbar_btn-video:last-child')) - var popup = editor.ownerDocument.querySelector('.jodit_toolbar_popup'); + var popup = editor.ownerDocument.querySelector('.jodit_toolbar_popup[data-editor_id=' + editor.id + ']'); - expect(popup && popup.style.display === 'block').to.be.equal(true); + expect(popup).to.be.not.equal(null); var positionPopup = offset(popup); var positionContainer = offset(editor.container); - expect(true).to.be.equal(positionPopup.left + positionPopup.width <= positionContainer.left + positionContainer.width); - // getBox().style.width = 'auto'; + expect(Math.abs((positionPopup.left + positionPopup.width) - (positionContainer.left + positionContainer.width)) < 2).to.be.true; }); }); }); @@ -168,7 +167,7 @@ describe('Test interface', function() { expect(list && window.getComputedStyle(list).display === 'block').to.equal(true); - simulateEvent('mousedown', 0, window) + simulateEvent('mousedown', 0, window); expect(list && list.parentNode === null).to.equal(true); }); diff --git a/test/tests/pluginsTest.js b/test/tests/pluginsTest.js index 84154c011..46ca051d2 100644 --- a/test/tests/pluginsTest.js +++ b/test/tests/pluginsTest.js @@ -1524,6 +1524,75 @@ describe('Test plugins', function () { expect('start test test test elm').to.be.equal(editor.getEditorValue().replace('', '').replace('', '')); }); }); + describe('Deny tags', function () { + describe('Parameter like string', function () { + it('Should remove all tags in denyTags options', function () { + var editor = new Jodit(appendTestArea(), { + cleanHTML: { + denyTags: 'p' + }, + }); + editor.value = '

    testopst

    pop

    '; + expect(editor.value).to.be.equal('

    pop

    '); + }); + }); + }); + describe('Allow tags', function () { + describe('Parameter like string', function () { + it('Should remove all tags not in allowTags options', function () { + var editor = new Jodit(appendTestArea(), { + cleanHTML: { + allowTags: 'p' + }, + }); + editor.value = '

    testopst

    pop

    '; + expect(editor.value).to.be.equal('

    test

    '); + }); + }); + describe('Parameter like hash', function () { + it('Should remove all tags not in allowTags options', function () { + var editor = new Jodit(appendTestArea(), { + cleanHTML: { + allowTags: { + p: true + } + }, + }); + editor.value = '

    testopst

    pop

    '; + expect(editor.value).to.be.equal('

    test

    '); + }); + }); + describe('Allow attributes', function () { + it('Should remove all attributes from element and remove not in allowTags options', function () { + var editor = new Jodit(appendTestArea(), { + cleanHTML: { + allowTags: { + p: { + style: true + } + } + }, + }); + editor.value = '

    testopst

    pop

    '; + expect(editor.value).to.be.equal('

    test

    '); + }); + }); + describe('Time checking', function () { + it('Should work fast', function () { + var editor = new Jodit(appendTestArea(), { + cleanHTML: { + allowTags: { + p: { + style: true + } + } + }, + }); + editor.value = '

    testopst

    pop

    '.repeat(500); + expect(editor.value).to.be.equal('

    test

    '.repeat(500)); + }).timeout(700); + }); + }); }); describe('Size plugin', function () { describe('In iframe mode after change mode', function () { diff --git a/test/tests/selectionTest.js b/test/tests/selectionTest.js index 3c64dbca4..97db93f61 100644 --- a/test/tests/selectionTest.js +++ b/test/tests/selectionTest.js @@ -614,6 +614,9 @@ describe('Selection Module Tests', function() { expect(['#text'].toString().toLowerCase()).to.be.equal(nodesNames.toString().toLowerCase()); }); + describe('If selected element is UL or LI or content in LI', function () { + + }); }); afterEach(function () { var i, keys = Object.keys(Jodit.instances); diff --git a/test/tests/tableTest.js b/test/tests/tableTest.js index b0b19b05c..9e303e48a 100644 --- a/test/tests/tableTest.js +++ b/test/tests/tableTest.js @@ -953,11 +953,15 @@ describe('Tables Jodit Editor Tests', function() { }); describe('Resize column', function () { describe('Move mouse over edge of cell', function () { - var brs = []; - for (i =0; i < 100; i += 1) { - brs.push(document.createElement('br')); - document.body.appendChild(brs[brs.length - 1]) - } + before(function () { + var brs = []; + for (i =0; i < 100; i += 1) { + brs.push(document.createElement('br')); + brs[brs.length - 1].classList.add('test'); + document.body.appendChild(brs[brs.length - 1]) + } + }) + describe('Normal scroll', function () { it('should show resizer element', function (done) { @@ -999,7 +1003,7 @@ describe('Tables Jodit Editor Tests', function() { }); }); after(function () { - brs.forEach(function (br) { + [].slice.call(document.querySelectorAll('br.test')).forEach(function (br) { br.parentNode && br.parentNode.removeChild(br) }); });