diff --git a/_develop/browsers.js b/_develop/browsers.js index 51026d1af1..4bc6324389 100644 --- a/_develop/browsers.js +++ b/_develop/browsers.js @@ -1,37 +1,42 @@ -var desktop = { - 'mac-chrome-latest' : ['OS X 10.12', 'chrome', '60.0'], - 'mac-firefox-latest' : ['OS X 10.12', 'firefox', '54.0'], - 'mac-safari-latest' : ['OS X 10.12', 'safari', '10.0'], - 'mac-chrome-previous' : ['OS X 10.11', 'chrome', '59.0'], - 'mac-firefox-previous' : ['OS X 10.11', 'firefox', '53.0'], - 'mac-safari-previous' : ['OS X 10.11', 'safari', '9.0'], +const desktop = { + 'mac-chrome-latest': ['OS X 10.12', 'chrome', '60.0'], + 'mac-firefox-latest': ['OS X 10.12', 'firefox', '54.0'], + 'mac-safari-latest': ['OS X 10.12', 'safari', '10.0'], + 'mac-chrome-previous': ['OS X 10.11', 'chrome', '59.0'], + 'mac-firefox-previous': ['OS X 10.11', 'firefox', '53.0'], + 'mac-safari-previous': ['OS X 10.11', 'safari', '9.0'], - 'windows-chrome-latest' : ['Windows 10', 'chrome', '60.0'], - 'windows-firefox-latest' : ['Windows 10', 'firefox', '54.0'], - 'windows-edge-latest' : ['Windows 10', 'microsoftedge', '15.15063'], - 'windows-chrome-previous' : ['Windows 8.1', 'chrome', '59.0'], + 'windows-chrome-latest': ['Windows 10', 'chrome', '60.0'], + 'windows-firefox-latest': ['Windows 10', 'firefox', '54.0'], + 'windows-edge-latest': ['Windows 10', 'microsoftedge', '15.15063'], + 'windows-chrome-previous': ['Windows 8.1', 'chrome', '59.0'], 'windows-firefox-previous': ['Windows 8.1', 'firefox', '53.0'], - 'windows-edge-previous' : ['Windows 10', 'microsoftedge', '14.14393'], + 'windows-edge-previous': ['Windows 10', 'microsoftedge', '14.14393'], }; -var mobile = { - 'ios-latest' : ['iPhone 7 Plus', 'iOS', '10.3', 'Safari'], - 'ios-previous' : ['iPhone 6 Plus', 'iOS', '9.3', 'Safari'], +const mobile = { + 'ios-latest': ['iPhone 7 Plus', 'iOS', '10.3', 'Safari'], + 'ios-previous': ['iPhone 6 Plus', 'iOS', '9.3', 'Safari'], - 'android-latest' : ['Android GoogleAPI Emulator', 'Android', '7.1', 'Chrome'], - 'android-previous' : ['Android GoogleAPI Emulator', 'Android', '6.0', 'Chrome'] + 'android-latest': ['Android GoogleAPI Emulator', 'Android', '7.1', 'Chrome'], + 'android-previous': [ + 'Android GoogleAPI Emulator', + 'Android', + '6.0', + 'Chrome', + ], }; -Object.keys(desktop).forEach(function(key) { +Object.keys(desktop).forEach(key => { module.exports[key] = { base: 'SauceLabs', browserName: desktop[key][1], version: desktop[key][2], - platform: desktop[key][0] + platform: desktop[key][0], }; }); -Object.keys(mobile).forEach(function(key) { +Object.keys(mobile).forEach(key => { module.exports[key] = { base: 'SauceLabs', browserName: mobile[key][3], @@ -39,6 +44,6 @@ Object.keys(mobile).forEach(function(key) { deviceName: mobile[key][0], deviceOrientation: 'portrait', platformVersion: mobile[key][2], - platformName: mobile[key][1] + platformName: mobile[key][1], }; }); diff --git a/_develop/karma.config.js b/_develop/karma.config.js index a562f7e80c..38077cf553 100644 --- a/_develop/karma.config.js +++ b/_develop/karma.config.js @@ -1,21 +1,25 @@ -var browsers = require('./browsers'); -var sauce = require('./sauce'); +const browsers = require('./browsers'); +const sauce = require('./sauce'); - -module.exports = function(config) { +module.exports = config => { config.set({ basePath: '../', urlRoot: '/karma/', port: process.env.npm_package_config_ports_karma, files: [ + { + pattern: + 'http://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.js', + served: true, + }, { pattern: 'dist/quill.snow.css', nocache: true }, { pattern: 'dist/unit.js', nocache: true }, { pattern: 'dist/*.map', included: false, served: true, nocache: true }, - { pattern: 'assets/favicon.png', included: false, served: true } + { pattern: 'assets/favicon.png', included: false, served: true }, ], proxies: { - '/assets/': '/karma/base/assets/' + '/assets/': '/karma/base/assets/', }, frameworks: ['jasmine'], @@ -26,30 +30,28 @@ module.exports = function(config) { browsers: ['Chrome'], client: { - useIframe: false + useIframe: false, }, coverageReporter: { dir: '.coverage', - reporters: [ - { type: 'text' }, - { type: 'html' } - ] + reporters: [{ type: 'text' }, { type: 'html' }], }, sauceLabs: { testName: 'quill-unit', options: { - 'public': 'public', - 'record-screenshots': false + public: 'public', + 'record-screenshots': false, }, build: sauce.build, username: sauce.username, accessKey: sauce.accessKey, - tunnelIdentifier: sauce.tunnel + tunnelIdentifier: sauce.tunnel, }, - customLaunchers: browsers + customLaunchers: browsers, }); + /* eslint-disable no-param-reassign */ if (process.env.TRAVIS) { config.sauceLabs.startConnect = false; config.transports = ['polling']; @@ -59,9 +61,11 @@ module.exports = function(config) { config.browserNoActivityTimeout = 60000; config.captureTimeout = 120000; // MS Edge does not work in an iframe - if (process.env.BROWSER.indexOf('ios') > -1 || - process.env.BROWSER.indexOf('android') > -1 || - process.env.BROWSER.indexOf('firefox') > -1) { + if ( + process.env.BROWSER.indexOf('ios') > -1 || + process.env.BROWSER.indexOf('android') > -1 || + process.env.BROWSER.indexOf('firefox') > -1 + ) { config.client.useIframe = true; } } diff --git a/_develop/proxy.js b/_develop/proxy.js index 6f039ee2ad..6d7828e8db 100644 --- a/_develop/proxy.js +++ b/_develop/proxy.js @@ -1,33 +1,41 @@ -var http = require('http'); -var httpProxy = require('http-proxy'); +const http = require('http'); +const httpProxy = require('http-proxy'); -var proxy = httpProxy.createProxyServer({}); -var ports = { - proxy: parseInt(process.env.npm_package_config_ports_proxy), - jekyll: parseInt(process.env.npm_package_config_ports_jekyll), - karma: parseInt(process.env.npm_package_config_ports_karma), - webpack: parseInt(process.env.npm_package_config_ports_webpack) +const proxy = httpProxy.createProxyServer({}); +const ports = { + proxy: parseInt(process.env.npm_package_config_ports_proxy, 10), + jekyll: parseInt(process.env.npm_package_config_ports_jekyll, 10), + karma: parseInt(process.env.npm_package_config_ports_karma, 10), + webpack: parseInt(process.env.npm_package_config_ports_webpack, 10), }; -var server = http.createServer(function(req, res) { - if (/\/\d+\.\d+\.\d+/.test(req.url) || req.url.startsWith('/karma/base/dist')) { - var target = 'http://localhost:' + ports.webpack + '/' + req.url.split('/').pop(); +const server = http.createServer((req, res) => { + if ( + /\/\d+\.\d+\.\d+/.test(req.url) || + req.url.startsWith('/karma/base/dist') + ) { + const target = `http://localhost:${ports.webpack}/${req.url + .split('/') + .pop()}`; proxy.web(req, res, { ignorePath: true, - target: target + target, }); - } else if (req.url.startsWith('/karma') || req.url === '/assets/favicon.png') { + } else if ( + req.url.startsWith('/karma') || + req.url === '/assets/favicon.png' + ) { proxy.web(req, res, { ignorePath: false, target: { port: ports.karma } }); } else { proxy.web(req, res, { ignorePath: false, target: { port: ports.jekyll } }); } }); -server.on('upgrade', function (req, socket, head) { +server.on('upgrade', (req, socket, head) => { proxy.ws(req, socket, head); }); -proxy.on('error', function(e) { +proxy.on('error', e => { console.error(e); }); diff --git a/_develop/sauce.js b/_develop/sauce.js index 777e1ad474..f8448d5ead 100644 --- a/_develop/sauce.js +++ b/_develop/sauce.js @@ -1,20 +1,15 @@ -var _ = require('lodash'); -var os = require('os'); - -var options = { - username: process.env.SAUCE_USERNAME, - accessKey: process.env.SAUCE_ACCESS_KEY -}; +const _ = require('lodash'); +const os = require('os'); if (process.env.TRAVIS) { module.exports = { build: process.env.TRAVIS_BUILD_ID, - tunnel: process.env.TRAVIS_JOB_NUMBER + tunnel: process.env.TRAVIS_JOB_NUMBER, }; } else { - var id = _.random(16*16*16*16).toString(16); + const id = _.random(16 * 16 * 16 * 16).toString(16); module.exports = { - build: os.hostname() + '-' + id, - tunnel: os.hostname() + '-tunnel-' + id + build: `${os.hostname()}-${id}`, + tunnel: `${os.hostname()}-tunnel-${id}`, }; } diff --git a/_develop/wdio.config.js b/_develop/wdio.config.js index f43f668415..f6eab1d481 100644 --- a/_develop/wdio.config.js +++ b/_develop/wdio.config.js @@ -1,21 +1,21 @@ exports.config = { - specs: [ - './test/functional/epic.js' - ], + specs: ['./test/functional/epic.js'], exclude: [], reporters: ['spec'], maxInstances: 10, - capabilities: [{ - browserName: 'chrome' - }], + capabilities: [ + { + browserName: 'chrome', + }, + ], sync: true, logLevel: 'error', coloredLogs: true, - baseUrl: 'http://localhost:' + process.env.npm_package_config_ports_proxy, + baseUrl: `http://localhost:${process.env.npm_package_config_ports_proxy}`, waitforTimeout: 10000, connectionRetryTimeout: 90000, @@ -24,9 +24,11 @@ exports.config = { framework: 'jasmine', jasmineNodeOpts: { defaultTimeoutInterval: 10000, - expectationResultHandler: function(passed, assertion) { + expectationResultHandler: passed => { if (passed) return; - this.saveScreenshot('./wd-' + this.desiredCapabilities.browserName + '-error.png'); - } - } -} + this.saveScreenshot( + `./wd-${this.desiredCapabilities.browserName}-error.png`, + ); + }, + }, +}; diff --git a/_develop/webpack.config.js b/_develop/webpack.config.js index 753ed0b28d..0faff4bce2 100644 --- a/_develop/webpack.config.js +++ b/_develop/webpack.config.js @@ -1,21 +1,22 @@ -var path = require('path'); -var pkg = require('../package.json'); -var webpack = require('webpack'); -var ExtractTextPlugin = require('extract-text-webpack-plugin'); +const path = require('path'); +const webpack = require('webpack'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const pkg = require('../package.json'); -var bannerPack = new webpack.BannerPlugin({ - banner: - 'Quill Editor v' + pkg.version + '\n' + - 'https://quilljs.com/\n' + - 'Copyright (c) 2014, Jason Chen\n' + +const bannerPack = new webpack.BannerPlugin({ + banner: [ + `Quill Editor v${pkg.version}`, + 'https://quilljs.com/', + 'Copyright (c) 2014, Jason Chen', 'Copyright (c) 2013, salesforce.com', - entryOnly: true + ].join('\n'), + entryOnly: true, }); -var constantPack = new webpack.DefinePlugin({ - QUILL_VERSION: JSON.stringify(pkg.version) +const constantPack = new webpack.DefinePlugin({ + QUILL_VERSION: JSON.stringify(pkg.version), }); -var source = [ +const source = [ 'quill.js', 'core.js', 'blots', @@ -24,14 +25,13 @@ var source = [ 'modules', 'test', 'themes', - 'ui' -].map(function(file) { + 'ui', +].map(file => { return path.resolve(__dirname, '..', file); }); - -module.exports = function(env) { - let config = { +module.exports = env => { + const config = { context: path.resolve(__dirname, '..'), entry: { 'quill.js': ['./quill.js'], @@ -39,110 +39,122 @@ module.exports = function(env) { 'quill.core': './assets/core.styl', 'quill.bubble': './assets/bubble.styl', 'quill.snow': './assets/snow.styl', - 'unit.js': './test/unit.js' + 'unit.js': './test/unit.js', }, output: { filename: '[name]', library: 'Quill', libraryExport: 'default', libraryTarget: 'umd', - path: path.resolve(__dirname, '../dist/') + path: path.resolve(__dirname, '../dist/'), }, resolve: { alias: { - 'parchment': path.resolve(__dirname, '../node_modules/parchment/src/parchment') + parchment: path.resolve( + __dirname, + '../node_modules/parchment/src/parchment', + ), }, - extensions: ['.js', '.styl', '.ts'] + extensions: ['.js', '.styl', '.ts'], }, module: { - rules: [{ - test: /\.ts$/, - use: [{ - loader: 'ts-loader', - options: { - compilerOptions: { - declaration: false, - target: 'es5', - module: 'commonjs' + rules: [ + { + test: /\.ts$/, + use: [ + { + loader: 'ts-loader', + options: { + compilerOptions: { + declaration: false, + target: 'es5', + module: 'commonjs', + }, + transpileOnly: true, + }, + }, + ], + }, + { + test: /\.styl$/, + include: [path.resolve(__dirname, '../assets')], + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: ['css-loader', 'stylus-loader'], + }), + }, + { + test: /\.svg$/, + include: [path.resolve(__dirname, '../assets/icons')], + use: [ + { + loader: 'html-loader', + options: { + minimize: true, + }, }, - transpileOnly: true - } - }] - }, { - test: /\.styl$/, - include: [ - path.resolve(__dirname, '../assets') - ], - use: ExtractTextPlugin.extract({ - fallback: 'style-loader', + ], + }, + { + test: /\.js$/, + include: source, use: [ - 'css-loader', - 'stylus-loader' - ] - }) - }, { - test: /\.svg$/, - include: [ - path.resolve(__dirname, '../assets/icons') - ], - use: [{ - loader: 'html-loader', - options: { - minimize: true - } - }] - }, { - test: /\.js$/, - include: source, - use: [{ - loader: 'babel-loader', - options: { - presets: [['env', { - targets: { - browsers: [ - 'last 2 Chrome major versions', - 'last 2 Firefox major versions', - 'last 2 Safari major versions', - 'last 2 Edge major versions', - 'last 2 iOS major versions', - 'last 2 ChromeAndroid major versions', - ] - } - }]] - } - }] - }], + { + loader: 'babel-loader', + options: { + presets: [ + [ + 'env', + { + targets: { + browsers: [ + 'last 2 Chrome major versions', + 'last 2 Firefox major versions', + 'last 2 Safari major versions', + 'last 2 Edge major versions', + 'last 2 iOS major versions', + 'last 2 ChromeAndroid major versions', + ], + }, + }, + ], + ], + }, + }, + ], + }, + ], noParse: [ /\/node_modules\/clone\/clone\.js$/, /\/node_modules\/eventemitter3\/index\.js$/, - /\/node_modules\/extend\/index\.js$/ - ] + /\/node_modules\/extend\/index\.js$/, + ], }, plugins: [ bannerPack, constantPack, new ExtractTextPlugin({ filename: '[name].css', - allChunks: true - }) + allChunks: true, + }), ], devServer: { contentBase: path.resolve(__dirname, '../dist'), hot: false, port: process.env.npm_package_config_ports_webpack, stats: 'minimal', - disableHostCheck: true - } + disableHostCheck: true, + }, }; if (env && env.minimize) { config.entry = { - 'quill.min.js': './quill.js' + 'quill.min.js': './quill.js', }; config.plugins.push( new webpack.optimize.UglifyJsPlugin({ - sourceMap: true - }) + sourceMap: true, + }), ); config.devtool = 'source-map'; } diff --git a/blots/block.js b/blots/block.js index 84879ae7da..67c4dfd111 100644 --- a/blots/block.js +++ b/blots/block.js @@ -5,45 +5,8 @@ import Break from './break'; import Inline from './inline'; import TextBlot from './text'; - const NEWLINE_LENGTH = 1; - -class BlockEmbed extends Parchment.Embed { - attach() { - super.attach(); - this.attributes = new Parchment.Attributor.Store(this.domNode); - } - - delta() { - return new Delta().insert(this.value(), extend(this.formats(), this.attributes.values())); - } - - format(name, value) { - let attribute = Parchment.query(name, Parchment.Scope.BLOCK_ATTRIBUTE); - if (attribute != null) { - this.attributes.attribute(attribute, value); - } - } - - formatAt(index, length, name, value) { - this.format(name, value); - } - - insertAt(index, value, def) { - if (typeof value === 'string' && value.endsWith('\n')) { - let block = Parchment.create(Block.blotName); - this.parent.insertBefore(block, index === 0 ? this : this.next); - block.insertAt(0, value.slice(0, -1)); - } else { - super.insertAt(index, value, def); - } - } -} -BlockEmbed.scope = Parchment.Scope.BLOCK_BLOT; -// It is important for cursor behavior BlockEmbeds use tags that are block level elements - - class Block extends Parchment.Block { constructor(domNode) { super(domNode); @@ -52,13 +15,14 @@ class Block extends Parchment.Block { delta() { if (this.cache.delta == null) { - this.cache.delta = this.descendants(Parchment.Leaf).reduce((delta, leaf) => { - if (leaf.length() === 0) { - return delta; - } else { + this.cache.delta = this.descendants(Parchment.Leaf) + .reduce((delta, leaf) => { + if (leaf.length() === 0) { + return delta; + } return delta.insert(leaf.value(), bubbleFormats(leaf)); - } - }, new Delta()).insert('\n', bubbleFormats(this)); + }, new Delta()) + .insert('\n', bubbleFormats(this)); } return this.cache.delta; } @@ -75,16 +39,24 @@ class Block extends Parchment.Block { this.format(name, value); } } else { - super.formatAt(index, Math.min(length, this.length() - index - 1), name, value); + super.formatAt( + index, + Math.min(length, this.length() - index - 1), + name, + value, + ); } this.cache = {}; } insertAt(index, value, def) { - if (def != null) return super.insertAt(index, value, def); + if (def != null) { + super.insertAt(index, value, def); + return; + } if (value.length === 0) return; - let lines = value.split('\n'); - let text = lines.shift(); + const lines = value.split('\n'); + const text = lines.shift(); if (text.length > 0) { if (index < this.length() - 1 || this.children.tail == null) { super.insertAt(Math.min(index, this.length() - 1), text); @@ -94,15 +66,15 @@ class Block extends Parchment.Block { this.cache = {}; } let block = this; - lines.reduce(function(index, line) { - block = block.split(index, true); + lines.reduce((lineIndex, line) => { + block = block.split(lineIndex, true); block.insertAt(0, line); return line.length; }, index + text.length); } insertBefore(blot, ref) { - let head = this.children.head; + const { head } = this.children; super.insertBefore(blot, ref); if (head instanceof Break) { head.remove(); @@ -138,19 +110,17 @@ class Block extends Parchment.Block { split(index, force = false) { if (force && (index === 0 || index >= this.length() - NEWLINE_LENGTH)) { - let clone = this.clone(); + const clone = this.clone(); if (index === 0) { this.parent.insertBefore(clone, this); return this; - } else { - this.parent.insertBefore(clone, this.next); - return clone; } - } else { - let next = super.split(index, force); - this.cache = {}; - return next; + this.parent.insertBefore(clone, this.next); + return clone; } + const next = super.split(index, force); + this.cache = {}; + return next; } } Block.blotName = 'block'; @@ -158,17 +128,56 @@ Block.tagName = 'P'; Block.defaultChild = 'break'; Block.allowedChildren = [Inline, Parchment.Embed, TextBlot]; +class BlockEmbed extends Parchment.Embed { + attach() { + super.attach(); + this.attributes = new Parchment.Attributor.Store(this.domNode); + } + + delta() { + return new Delta().insert( + this.value(), + extend(this.formats(), this.attributes.values()), + ); + } + + format(name, value) { + const attribute = Parchment.query(name, Parchment.Scope.BLOCK_ATTRIBUTE); + if (attribute != null) { + this.attributes.attribute(attribute, value); + } + } + + formatAt(index, length, name, value) { + this.format(name, value); + } + + insertAt(index, value, def) { + if (typeof value === 'string' && value.endsWith('\n')) { + const block = Parchment.create(Block.blotName); + this.parent.insertBefore(block, index === 0 ? this : this.next); + block.insertAt(0, value.slice(0, -1)); + } else { + super.insertAt(index, value, def); + } + } +} +BlockEmbed.scope = Parchment.Scope.BLOCK_BLOT; +// It is important for cursor behavior BlockEmbeds use tags that are block level elements function bubbleFormats(blot, formats = {}) { if (blot == null) return formats; if (typeof blot.formats === 'function') { formats = extend(formats, blot.formats()); } - if (blot.parent == null || blot.parent.blotName == 'scroll' || blot.parent.statics.scope !== blot.statics.scope) { + if ( + blot.parent == null || + blot.parent.blotName === 'scroll' || + blot.parent.statics.scope !== blot.statics.scope + ) { return formats; } return bubbleFormats(blot.parent, formats); } - export { bubbleFormats, BlockEmbed, Block as default }; diff --git a/blots/break.js b/blots/break.js index fcdbc26532..330a64f13c 100644 --- a/blots/break.js +++ b/blots/break.js @@ -1,6 +1,5 @@ import Parchment from 'parchment'; - class Break extends Parchment.Embed { static value() { return undefined; @@ -25,5 +24,4 @@ class Break extends Parchment.Embed { Break.blotName = 'break'; Break.tagName = 'BR'; - export default Break; diff --git a/blots/container.js b/blots/container.js index 007e804f3a..4febf12001 100644 --- a/blots/container.js +++ b/blots/container.js @@ -1,9 +1,7 @@ import Parchment from 'parchment'; import Block, { BlockEmbed } from './block'; - -class Container extends Parchment.Container { } +class Container extends Parchment.Container {} Container.allowedChildren = [Block, BlockEmbed, Container]; - export default Container; diff --git a/blots/cursor.js b/blots/cursor.js index c650d973ab..3a1f948f1c 100644 --- a/blots/cursor.js +++ b/blots/cursor.js @@ -1,7 +1,6 @@ import Parchment from 'parchment'; import TextBlot from './text'; - class Cursor extends Parchment.Embed { static value() { return undefined; @@ -12,7 +11,7 @@ class Cursor extends Parchment.Embed { this.selection = selection; this.textNode = document.createTextNode(Cursor.CONTENTS); this.domNode.appendChild(this.textNode); - this._length = 0; + this.savedLength = 0; } detach() { @@ -21,19 +20,24 @@ class Cursor extends Parchment.Embed { } format(name, value) { - if (this._length !== 0) { - return super.format(name, value); + if (this.savedLength !== 0) { + super.format(name, value); + return; } - let target = this, index = 0; - while (target != null && target.statics.scope !== Parchment.Scope.BLOCK_BLOT) { + let target = this; + let index = 0; + while ( + target != null && + target.statics.scope !== Parchment.Scope.BLOCK_BLOT + ) { index += target.offset(target.parent); target = target.parent; } if (target != null) { - this._length = Cursor.CONTENTS.length; + this.savedLength = Cursor.CONTENTS.length; target.optimize(); target.formatAt(index, Cursor.CONTENTS.length, name, value); - this._length = 0; + this.savedLength = 0; } } @@ -43,7 +47,7 @@ class Cursor extends Parchment.Embed { } length() { - return this._length; + return this.savedLength; } position() { @@ -56,19 +60,34 @@ class Cursor extends Parchment.Embed { } restore() { - if (this.selection.composing || this.parent == null) return; - let textNode = this.textNode; - let range = this.selection.getNativeRange(); - let restoreText, start, end; - if (range != null && range.start.node === textNode && range.end.node === textNode) { - [restoreText, start, end] = [textNode, range.start.offset, range.end.offset]; + if (this.selection.composing || this.parent == null) return null; + const range = this.selection.getNativeRange(); + let restoreText; + let start; + let end; + if ( + range != null && + range.start.node === this.textNode && + range.end.node === this.textNode + ) { + [restoreText, start, end] = [ + this.textNode, + range.start.offset, + range.end.offset, + ]; } // Link format will insert text outside of anchor tag - while (this.domNode.lastChild != null && this.domNode.lastChild !== this.textNode) { - this.domNode.parentNode.insertBefore(this.domNode.lastChild, this.domNode); + while ( + this.domNode.lastChild != null && + this.domNode.lastChild !== this.textNode + ) { + this.domNode.parentNode.insertBefore( + this.domNode.lastChild, + this.domNode, + ); } if (this.textNode.data !== Cursor.CONTENTS) { - let text = this.textNode.data.split(Cursor.CONTENTS).join(''); + const text = this.textNode.data.split(Cursor.CONTENTS).join(''); if (this.next instanceof TextBlot) { restoreText = this.next.domNode; this.next.insertAt(0, text); @@ -82,23 +101,28 @@ class Cursor extends Parchment.Embed { } this.remove(); if (start != null) { - [start, end] = [start, end].map(function(offset) { + [start, end] = [start, end].map(offset => { return Math.max(0, Math.min(restoreText.data.length, offset - 1)); }); return { startNode: restoreText, startOffset: start, endNode: restoreText, - endOffset: end + endOffset: end, }; } + return null; } update(mutations, context) { - if (mutations.some((mutation) => { - return mutation.type === 'characterData' && mutation.target === this.textNode; - })) { - let range = this.restore(); + if ( + mutations.some(mutation => { + return ( + mutation.type === 'characterData' && mutation.target === this.textNode + ); + }) + ) { + const range = this.restore(); if (range) context.range = range; } } @@ -110,7 +134,6 @@ class Cursor extends Parchment.Embed { Cursor.blotName = 'cursor'; Cursor.className = 'ql-cursor'; Cursor.tagName = 'span'; -Cursor.CONTENTS = "\uFEFF"; // Zero width no break space - +Cursor.CONTENTS = '\uFEFF'; // Zero width no break space export default Cursor; diff --git a/blots/embed.js b/blots/embed.js index f079cda1b1..c9d3a539fb 100644 --- a/blots/embed.js +++ b/blots/embed.js @@ -1,15 +1,14 @@ import Parchment from 'parchment'; import TextBlot from './text'; -const GUARD_TEXT = "\uFEFF"; - +const GUARD_TEXT = '\uFEFF'; class Embed extends Parchment.Embed { constructor(node) { super(node); this.contentNode = document.createElement('span'); this.contentNode.setAttribute('contenteditable', false); - [].slice.call(this.domNode.childNodes).forEach((childNode) => { + [].slice.call(this.domNode.childNodes).forEach(childNode => { this.contentNode.appendChild(childNode); }); this.leftGuard = document.createTextNode(GUARD_TEXT); @@ -26,22 +25,23 @@ class Embed extends Parchment.Embed { } restore(node) { - let range, textNode; - let text = node.data.split(GUARD_TEXT).join(''); + let range; + let textNode; + const text = node.data.split(GUARD_TEXT).join(''); if (node === this.leftGuard) { if (this.prev instanceof TextBlot) { - let prevLength = this.prev.length(); + const prevLength = this.prev.length(); this.prev.insertAt(prevLength, text); range = { startNode: this.prev.domNode, - startOffset: prevLength + text.length + startOffset: prevLength + text.length, }; } else { textNode = document.createTextNode(text); this.parent.insertBefore(Parchment.create(textNode), this); range = { startNode: textNode, - startOffset: text.length + startOffset: text.length, }; } } else if (node === this.rightGuard) { @@ -49,14 +49,14 @@ class Embed extends Parchment.Embed { this.next.insertAt(0, text); range = { startNode: this.next.domNode, - startOffset: text.length - } + startOffset: text.length, + }; } else { textNode = document.createTextNode(text); this.parent.insertBefore(Parchment.create(textNode), this.next); range = { startNode: textNode, - startOffset: text.length + startOffset: text.length, }; } } @@ -65,15 +65,17 @@ class Embed extends Parchment.Embed { } update(mutations, context) { - mutations.forEach((mutation) => { - if (mutation.type === 'characterData' && - (mutation.target === this.leftGuard || mutation.target === this.rightGuard)) { - let range = this.restore(mutation.target); + mutations.forEach(mutation => { + if ( + mutation.type === 'characterData' && + (mutation.target === this.leftGuard || + mutation.target === this.rightGuard) + ) { + const range = this.restore(mutation.target); if (range) context.range = range; } }); } } - export default Embed; diff --git a/blots/inline.js b/blots/inline.js index ee6889b2d3..7c26d867b3 100644 --- a/blots/inline.js +++ b/blots/inline.js @@ -1,25 +1,26 @@ -import Text from './text'; import Parchment from 'parchment'; - +import Text from './text'; class Inline extends Parchment.Inline { static compare(self, other) { - let selfIndex = Inline.order.indexOf(self); - let otherIndex = Inline.order.indexOf(other); + const selfIndex = Inline.order.indexOf(self); + const otherIndex = Inline.order.indexOf(other); if (selfIndex >= 0 || otherIndex >= 0) { return selfIndex - otherIndex; } else if (self === other) { return 0; } else if (self < other) { return -1; - } else { - return 1; } + return 1; } formatAt(index, length, name, value) { - if (Inline.compare(this.statics.blotName, name) < 0 && Parchment.query(name, Parchment.Scope.BLOT)) { - let blot = this.isolate(index, length); + if ( + Inline.compare(this.statics.blotName, name) < 0 && + Parchment.query(name, Parchment.Scope.BLOT) + ) { + const blot = this.isolate(index, length); if (value) { blot.wrap(name, value); } @@ -30,9 +31,11 @@ class Inline extends Parchment.Inline { optimize(context) { super.optimize(context); - if (this.parent instanceof Inline && - Inline.compare(this.statics.blotName, this.parent.statics.blotName) > 0) { - let parent = this.parent.isolate(this.offset(), this.length()); + if ( + this.parent instanceof Inline && + Inline.compare(this.statics.blotName, this.parent.statics.blotName) > 0 + ) { + const parent = this.parent.isolate(this.offset(), this.length()); this.moveChildren(parent); parent.wrap(this); } @@ -41,10 +44,15 @@ class Inline extends Parchment.Inline { Inline.allowedChildren = [Inline, Parchment.Embed, Text]; // Lower index means deeper in the DOM tree, since not found (-1) is for embeds Inline.order = [ - 'cursor', 'inline', // Must be lower - 'underline', 'strike', 'italic', 'bold', 'script', - 'link', 'code' // Must be higher + 'cursor', + 'inline', // Must be lower + 'underline', + 'strike', + 'italic', + 'bold', + 'script', + 'link', + 'code', // Must be higher ]; - export default Inline; diff --git a/blots/scroll.js b/blots/scroll.js index 8c5b9d52b9..8fab379fda 100644 --- a/blots/scroll.js +++ b/blots/scroll.js @@ -5,24 +5,22 @@ import Break from './break'; import CodeBlock from '../formats/code'; import Container from './container'; - function isLine(blot) { - return (blot instanceof Block || blot instanceof BlockEmbed); + return blot instanceof Block || blot instanceof BlockEmbed; } - class Scroll extends Parchment.Scroll { constructor(domNode, config) { super(domNode); this.emitter = config.emitter; if (Array.isArray(config.whitelist)) { - this.whitelist = config.whitelist.reduce(function(whitelist, format) { + this.whitelist = config.whitelist.reduce((whitelist, format) => { whitelist[format] = true; return whitelist; }, {}); } // Some reason fixes composition issues with character languages in Windows/Chrome, Safari - this.domNode.addEventListener('DOMNodeInserted', function() {}); + this.domNode.addEventListener('DOMNodeInserted', () => {}); this.optimize(); this.enable(); } @@ -37,8 +35,9 @@ class Scroll extends Parchment.Scroll { } deleteAt(index, length) { - let [first, offset] = this.line(index); - let [last, ] = this.line(index + length); + const [firstLine, offset] = this.line(index); + let first = firstLine; + const [last] = this.line(index + length); super.deleteAt(index, length); if (last != null && first !== last && offset > 0) { if (first instanceof BlockEmbed || last instanceof BlockEmbed) { @@ -46,7 +45,7 @@ class Scroll extends Parchment.Scroll { return; } if (first instanceof CodeBlock) { - let newlineIndex = first.newlineIndex(first.length(), true); + const newlineIndex = first.newlineIndex(first.length(), true); if (newlineIndex > -1) { first = first.split(newlineIndex + 1); if (first === last) { @@ -55,12 +54,13 @@ class Scroll extends Parchment.Scroll { } } } else if (last instanceof CodeBlock) { - let newlineIndex = last.newlineIndex(0); + const newlineIndex = last.newlineIndex(0); if (newlineIndex > -1) { last.split(newlineIndex + 1); } } - let ref = last.children.head instanceof Break ? null : last.children.head; + const ref = + last.children.head instanceof Break ? null : last.children.head; first.moveChildren(last, ref); first.remove(); } @@ -80,15 +80,19 @@ class Scroll extends Parchment.Scroll { insertAt(index, value, def) { if (def != null && this.whitelist != null && !this.whitelist[value]) return; if (index >= this.length()) { - if (def == null || Parchment.query(value, Parchment.Scope.BLOCK) == null) { - let blot = Parchment.create(this.statics.defaultChild); + if ( + def == null || + Parchment.query(value, Parchment.Scope.BLOCK) == null + ) { + const blot = Parchment.create(this.statics.defaultChild); this.appendChild(blot); if (def == null && value.endsWith('\n')) { - value = value.slice(0, -1); + blot.insertAt(0, value.slice(0, -1), def); + } else { + blot.insertAt(0, value, def); } - blot.insertAt(0, value, def); } else { - let embed = Parchment.create(value, def); + const embed = Parchment.create(value, def); this.appendChild(embed); } } else { @@ -99,11 +103,12 @@ class Scroll extends Parchment.Scroll { insertBefore(blot, ref) { if (blot.statics.scope === Parchment.Scope.INLINE_BLOT) { - let wrapper = Parchment.create(this.statics.defaultChild); + const wrapper = Parchment.create(this.statics.defaultChild); wrapper.appendChild(blot); - blot = wrapper; + super.insertBefore(wrapper, ref); + } else { + super.insertBefore(blot, ref); } - super.insertBefore(blot, ref); } leaf(index) { @@ -118,16 +123,21 @@ class Scroll extends Parchment.Scroll { } lines(index = 0, length = Number.MAX_VALUE) { - let getLines = (blot, index, length) => { - let lines = [], lengthLeft = length; - blot.children.forEachAt(index, length, function(child, index, length) { - if (isLine(child)) { - lines.push(child); - } else if (child instanceof Parchment.Container) { - lines = lines.concat(getLines(child, index, lengthLeft)); - } - lengthLeft -= length; - }); + const getLines = (blot, blotIndex, blotLength) => { + let lines = []; + let lengthLeft = blotLength; + blot.children.forEachAt( + blotIndex, + blotLength, + (child, childIndex, childLength) => { + if (isLine(child)) { + lines.push(child); + } else if (child instanceof Parchment.Container) { + lines = lines.concat(getLines(child, childIndex, lengthLeft)); + } + lengthLeft -= childLength; + }, + ); return lines; }; return getLines(this, index, length); @@ -142,7 +152,7 @@ class Scroll extends Parchment.Scroll { } path(index) { - return super.path(index).slice(1); // Exclude self + return super.path(index).slice(1); // Exclude self } update(mutations) { @@ -157,7 +167,7 @@ class Scroll extends Parchment.Scroll { if (mutations.length > 0) { this.emitter.emit(Emitter.events.SCROLL_BEFORE_UPDATE, source, mutations); } - super.update(mutations.concat([])); // pass copy + super.update(mutations.concat([])); // pass copy if (mutations.length > 0) { this.emitter.emit(Emitter.events.SCROLL_UPDATE, source, mutations); } @@ -169,5 +179,4 @@ Scroll.tagName = 'DIV'; Scroll.defaultChild = 'block'; Scroll.allowedChildren = [Block, BlockEmbed, Container]; - export default Scroll; diff --git a/core.js b/core.js index a01db5b47f..89affa339f 100644 --- a/core.js +++ b/core.js @@ -15,22 +15,21 @@ import History from './modules/history'; import Keyboard from './modules/keyboard'; Quill.register({ - 'blots/block' : Block, - 'blots/block/embed' : BlockEmbed, - 'blots/break' : Break, - 'blots/container' : Container, - 'blots/cursor' : Cursor, - 'blots/embed' : Embed, - 'blots/inline' : Inline, - 'blots/scroll' : Scroll, - 'blots/text' : TextBlot, + 'blots/block': Block, + 'blots/block/embed': BlockEmbed, + 'blots/break': Break, + 'blots/container': Container, + 'blots/cursor': Cursor, + 'blots/embed': Embed, + 'blots/inline': Inline, + 'blots/scroll': Scroll, + 'blots/text': TextBlot, - 'modules/clipboard' : Clipboard, - 'modules/history' : History, - 'modules/keyboard' : Keyboard + 'modules/clipboard': Clipboard, + 'modules/history': History, + 'modules/keyboard': Keyboard, }); Parchment.register(Block, Break, Cursor, Inline, Scroll, TextBlot); - export default Quill; diff --git a/core/editor.js b/core/editor.js index a6d1b1fe84..9e8e4e2036 100644 --- a/core/editor.js +++ b/core/editor.js @@ -1,3 +1,6 @@ +import clone from 'clone'; +import equal from 'deep-equal'; +import extend from 'extend'; import Delta from 'quill-delta'; import DeltaOp from 'quill-delta/lib/op'; import Parchment from 'parchment'; @@ -5,14 +8,9 @@ import CodeBlock from '../formats/code'; import CursorBlot from '../blots/cursor'; import Block, { bubbleFormats } from '../blots/block'; import Break from '../blots/break'; -import clone from 'clone'; -import equal from 'deep-equal'; -import extend from 'extend'; - const ASCII = /^[ -~]*$/; - class Editor { constructor(scroll) { this.scroll = scroll; @@ -24,9 +22,9 @@ class Editor { this.scroll.update(); let scrollLength = this.scroll.length(); this.scroll.batchStart(); - delta = normalizeDelta(delta); - delta.reduce((index, op) => { - let length = op.retain || op.delete || op.insert.length || 1; + const normalizedDelta = normalizeDelta(delta); + normalizedDelta.reduce((index, op) => { + const length = op.retain || op.delete || op.insert.length || 1; let attributes = op.attributes || {}; if (op.insert != null) { if (typeof op.insert === 'string') { @@ -39,26 +37,26 @@ class Editor { consumeNextNewline = true; } this.scroll.insertAt(index, text); - let [line, offset] = this.scroll.line(index); + const [line, offset] = this.scroll.line(index); let formats = extend({}, bubbleFormats(line)); if (line instanceof Block) { - let [leaf, ] = line.descendant(Parchment.Leaf, offset); + const [leaf] = line.descendant(Parchment.Leaf, offset); formats = extend(formats, bubbleFormats(leaf)); } attributes = DeltaOp.attributes.diff(formats, attributes) || {}; } else if (typeof op.insert === 'object') { - let key = Object.keys(op.insert)[0]; // There should only be one key + const key = Object.keys(op.insert)[0]; // There should only be one key if (key == null) return index; this.scroll.insertAt(index, key, op.insert[key]); } scrollLength += length; } - Object.keys(attributes).forEach((name) => { + Object.keys(attributes).forEach(name => { this.scroll.formatAt(index, length, name, attributes[name]); }); return index + length; }, 0); - delta.reduce((index, op) => { + normalizedDelta.reduce((index, op) => { if (typeof op.delete === 'number') { this.scroll.deleteAt(index, op.delete); return index; @@ -66,7 +64,7 @@ class Editor { return index + (op.retain || op.insert.length || 1); }, 0); this.scroll.batchEnd(); - return this.update(delta); + return this.update(normalizedDelta); } deleteText(index, length) { @@ -76,31 +74,37 @@ class Editor { formatLine(index, length, formats = {}) { this.scroll.update(); - Object.keys(formats).forEach((format) => { - if (this.scroll.whitelist != null && !this.scroll.whitelist[format]) return; - let lines = this.scroll.lines(index, Math.max(length, 1)); + Object.keys(formats).forEach(format => { + if (this.scroll.whitelist != null && !this.scroll.whitelist[format]) + return; + const lines = this.scroll.lines(index, Math.max(length, 1)); let lengthRemaining = length; - lines.forEach((line) => { - let lineLength = line.length(); + lines.forEach(line => { + const lineLength = line.length(); if (!(line instanceof CodeBlock)) { line.format(format, formats[format]); } else { - let codeIndex = index - line.offset(this.scroll); - let codeLength = line.newlineIndex(codeIndex + lengthRemaining) - codeIndex + 1; + const codeIndex = index - line.offset(this.scroll); + const codeLength = + line.newlineIndex(codeIndex + lengthRemaining) - codeIndex + 1; line.formatAt(codeIndex, codeLength, format, formats[format]); } lengthRemaining -= lineLength; }); }); this.scroll.optimize(); - return this.update(new Delta().retain(index).retain(length, clone(formats))); + return this.update( + new Delta().retain(index).retain(length, clone(formats)), + ); } formatText(index, length, formats = {}) { - Object.keys(formats).forEach((format) => { + Object.keys(formats).forEach(format => { this.scroll.formatAt(index, length, format, formats[format]); }); - return this.update(new Delta().retain(index).retain(length, clone(formats))); + return this.update( + new Delta().retain(index).retain(length, clone(formats)), + ); } getContents(index, length) { @@ -114,10 +118,11 @@ class Editor { } getFormat(index, length = 0) { - let lines = [], leaves = []; + let lines = []; + let leaves = []; if (length === 0) { - this.scroll.path(index).forEach(function(path) { - let [blot, ] = path; + this.scroll.path(index).forEach(path => { + const [blot] = path; if (blot instanceof Block) { lines.push(blot); } else if (blot instanceof Parchment.Leaf) { @@ -128,11 +133,11 @@ class Editor { lines = this.scroll.lines(index, length); leaves = this.scroll.descendants(Parchment.Leaf, index, length); } - let formatsArr = [lines, leaves].map(function(blots) { + const formatsArr = [lines, leaves].map(blots => { if (blots.length === 0) return {}; let formats = bubbleFormats(blots.shift()); while (Object.keys(formats).length > 0) { - let blot = blots.shift(); + const blot = blots.shift(); if (blot == null) return formats; formats = combineFormats(bubbleFormats(blot), formats); } @@ -142,11 +147,10 @@ class Editor { } getText(index, length) { - return this.getContents(index, length).filter(function(op) { - return typeof op.insert === 'string'; - }).map(function(op) { - return op.insert; - }).join(''); + return this.getContents(index, length) + .filter(op => typeof op.insert === 'string') + .map(op => op.insert) + .join(''); } insertEmbed(index, embed, value) { @@ -157,59 +161,66 @@ class Editor { insertText(index, text, formats = {}) { text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); this.scroll.insertAt(index, text); - Object.keys(formats).forEach((format) => { + Object.keys(formats).forEach(format => { this.scroll.formatAt(index, text.length, format, formats[format]); }); return this.update(new Delta().retain(index).insert(text, clone(formats))); } isBlank() { - if (this.scroll.children.length == 0) return true; + if (this.scroll.children.length === 0) return true; if (this.scroll.children.length > 1) return false; - let block = this.scroll.children.head; + const block = this.scroll.children.head; if (block.statics.blotName !== Block.blotName) return false; if (block.children.length > 1) return false; return block.children.head instanceof Break; } removeFormat(index, length) { - let text = this.getText(index, length); - let [line, offset] = this.scroll.line(index + length); - let suffixLength = 0, suffix = new Delta(); + const text = this.getText(index, length); + const [line, offset] = this.scroll.line(index + length); + let suffixLength = 0; + let suffix = new Delta(); if (line != null) { if (!(line instanceof CodeBlock)) { suffixLength = line.length() - offset; } else { suffixLength = line.newlineIndex(offset) - offset + 1; } - suffix = line.delta().slice(offset, offset + suffixLength - 1).insert('\n'); + suffix = line + .delta() + .slice(offset, offset + suffixLength - 1) + .insert('\n'); } - let contents = this.getContents(index, length + suffixLength); - let diff = contents.diff(new Delta().insert(text).concat(suffix)); - let delta = new Delta().retain(index).concat(diff); + const contents = this.getContents(index, length + suffixLength); + const diff = contents.diff(new Delta().insert(text).concat(suffix)); + const delta = new Delta().retain(index).concat(diff); return this.applyDelta(delta); } update(change, mutations = [], cursorIndex = undefined) { - let oldDelta = this.delta; - if (mutations.length === 1 && - mutations[0].type === 'characterData' && - mutations[0].target.data.match(ASCII) && - Parchment.find(mutations[0].target)) { + const oldDelta = this.delta; + if ( + mutations.length === 1 && + mutations[0].type === 'characterData' && + mutations[0].target.data.match(ASCII) && + Parchment.find(mutations[0].target) + ) { // Optimization for character changes - let textBlot = Parchment.find(mutations[0].target); - let formats = bubbleFormats(textBlot); - let index = textBlot.offset(this.scroll); - let oldValue = mutations[0].oldValue.replace(CursorBlot.CONTENTS, ''); - let oldText = new Delta().insert(oldValue); - let newText = new Delta().insert(textBlot.value()); - let diffDelta = new Delta().retain(index).concat(oldText.diff(newText, cursorIndex)); - change = diffDelta.reduce(function(delta, op) { + const textBlot = Parchment.find(mutations[0].target); + const formats = bubbleFormats(textBlot); + const index = textBlot.offset(this.scroll); + const oldValue = mutations[0].oldValue.replace(CursorBlot.CONTENTS, ''); + const oldText = new Delta().insert(oldValue); + const newText = new Delta().insert(textBlot.value()); + const diffDelta = new Delta() + .retain(index) + .concat(oldText.diff(newText, cursorIndex)); + change = diffDelta.reduce((delta, op) => { if (op.insert) { return delta.insert(op.insert, formats); - } else { - return delta.push(op); } + return delta.push(op); }, new Delta()); this.delta = oldDelta.compose(change); } else { @@ -222,9 +233,8 @@ class Editor { } } - function combineFormats(formats, combined) { - return Object.keys(combined).reduce(function(merged, name) { + return Object.keys(combined).reduce((merged, name) => { if (formats[name] == null) return merged; if (combined[name] === formats[name]) { merged[name] = combined[name]; @@ -240,13 +250,16 @@ function combineFormats(formats, combined) { } function normalizeDelta(delta) { - return delta.reduce(function(delta, op) { + return delta.reduce((normalizedDelta, op) => { if (op.insert === 1) { - let attributes = clone(op.attributes); - delete attributes['image']; - return delta.insert({ image: op.attributes.image }, attributes); + const attributes = clone(op.attributes); + delete attributes.image; + return normalizedDelta.insert({ image: op.attributes.image }, attributes); } - if (op.attributes != null && (op.attributes.list === true || op.attributes.bullet === true)) { + if ( + op.attributes != null && + (op.attributes.list === true || op.attributes.bullet === true) + ) { op = clone(op); if (op.attributes.list) { op.attributes.list = 'ordered'; @@ -256,12 +269,11 @@ function normalizeDelta(delta) { } } if (typeof op.insert === 'string') { - let text = op.insert.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); - return delta.insert(text, op.attributes); + const text = op.insert.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + return normalizedDelta.insert(text, op.attributes); } - return delta.push(op); + return normalizedDelta.push(op); }, new Delta()); } - export default Editor; diff --git a/core/emitter.js b/core/emitter.js index 5814f127c1..79b8e49386 100644 --- a/core/emitter.js +++ b/core/emitter.js @@ -1,14 +1,15 @@ import EventEmitter from 'eventemitter3'; import logger from './logger'; -let debug = logger('quill:events'); +const debug = logger('quill:events'); const EVENTS = ['selectionchange', 'mousedown', 'mouseup', 'click']; -EVENTS.forEach(function(eventName) { +EVENTS.forEach(eventName => { document.addEventListener(eventName, (...args) => { - [].slice.call(document.querySelectorAll('.ql-container')).forEach((node) => { + [].slice.call(document.querySelectorAll('.ql-container')).forEach(node => { // TODO use WeakMap + /* eslint-disable no-underscore-dangle */ if (node.__quill && node.__quill.emitter) { node.__quill.emitter.handleDOM(...args); } @@ -16,7 +17,6 @@ EVENTS.forEach(function(eventName) { }); }); - class Emitter extends EventEmitter { constructor() { super(); @@ -24,13 +24,13 @@ class Emitter extends EventEmitter { this.on('error', debug.error); } - emit() { - debug.log.apply(debug, arguments); - super.emit.apply(this, arguments); + emit(...args) { + debug.log.call(debug, ...args); + super.emit(...args); } handleDOM(event, ...args) { - (this.listeners[event.type] || []).forEach(function({ node, handler }) { + (this.listeners[event.type] || []).forEach(({ node, handler }) => { if (event.target === node || node.contains(event.target)) { handler(event, ...args); } @@ -41,23 +41,22 @@ class Emitter extends EventEmitter { if (!this.listeners[eventName]) { this.listeners[eventName] = []; } - this.listeners[eventName].push({ node, handler }) + this.listeners[eventName].push({ node, handler }); } } Emitter.events = { - EDITOR_CHANGE : 'editor-change', - SCROLL_BEFORE_UPDATE : 'scroll-before-update', - SCROLL_OPTIMIZE : 'scroll-optimize', - SCROLL_UPDATE : 'scroll-update', - SELECTION_CHANGE : 'selection-change', - TEXT_CHANGE : 'text-change' + EDITOR_CHANGE: 'editor-change', + SCROLL_BEFORE_UPDATE: 'scroll-before-update', + SCROLL_OPTIMIZE: 'scroll-optimize', + SCROLL_UPDATE: 'scroll-update', + SELECTION_CHANGE: 'selection-change', + TEXT_CHANGE: 'text-change', }; Emitter.sources = { - API : 'api', - SILENT : 'silent', - USER : 'user' + API: 'api', + SILENT: 'silent', + USER: 'user', }; - export default Emitter; diff --git a/core/logger.js b/core/logger.js index 1f62613df1..e121226bf3 100644 --- a/core/logger.js +++ b/core/logger.js @@ -1,22 +1,22 @@ -let levels = ['error', 'warn', 'log', 'info']; +const levels = ['error', 'warn', 'log', 'info']; let level = 'warn'; function debug(method, ...args) { if (levels.indexOf(method) <= levels.indexOf(level)) { - console[method](...args); // eslint-disable-line no-console + console[method](...args); // eslint-disable-line no-console } } function namespace(ns) { - return levels.reduce(function(logger, method) { + return levels.reduce((logger, method) => { logger[method] = debug.bind(console, method, ns); return logger; }, {}); } -debug.level = namespace.level = function(newLevel) { +namespace.level = newLevel => { level = newLevel; }; - +debug.level = namespace.level; export default namespace; diff --git a/core/module.js b/core/module.js index 24af66b8e3..6e835a0002 100644 --- a/core/module.js +++ b/core/module.js @@ -6,5 +6,4 @@ class Module { } Module.DEFAULTS = {}; - export default Module; diff --git a/core/quill.js b/core/quill.js index 42b31bcbe4..180c952992 100644 --- a/core/quill.js +++ b/core/quill.js @@ -1,15 +1,14 @@ import Delta from 'quill-delta'; +import Parchment from 'parchment'; +import extend from 'extend'; import Editor from './editor'; import Emitter from './emitter'; import Module from './module'; -import Parchment from 'parchment'; import Selection, { Range } from './selection'; -import extend from 'extend'; import logger from './logger'; import Theme from './theme'; -let debug = logger('quill'); - +const debug = logger('quill'); class Quill { static debug(limit) { @@ -20,6 +19,7 @@ class Quill { } static find(node) { + // eslint-disable-next-line no-underscore-dangle return node.__quill || Parchment.find(node); } @@ -32,12 +32,12 @@ class Quill { static register(path, target, overwrite = false) { if (typeof path !== 'string') { - let name = path.attrName || path.blotName; + const name = path.attrName || path.blotName; if (typeof name === 'string') { // register(Blot | Attributor, overwrite) - this.register('formats/' + name, path, target); + this.register(`formats/${name}`, path, target); } else { - Object.keys(path).forEach((key) => { + Object.keys(path).forEach(key => { this.register(key, path[key], target); }); } @@ -46,8 +46,10 @@ class Quill { debug.warn(`Overwriting ${path} with`, target); } this.imports[path] = target; - if ((path.startsWith('blots/') || path.startsWith('formats/')) && - target.blotName !== 'abstract') { + if ( + (path.startsWith('blots/') || path.startsWith('formats/')) && + target.blotName !== 'abstract' + ) { Parchment.register(target); } if (typeof target.register === 'function') { @@ -65,10 +67,10 @@ class Quill { if (this.options.debug) { Quill.debug(this.options.debug); } - let html = this.container.innerHTML.trim(); + const html = this.container.innerHTML.trim(); this.container.classList.add('ql-container'); this.container.innerHTML = ''; - this.container.__quill = this; + this.container.__quill = this; // eslint-disable-line no-underscore-dangle this.root = this.addContainer('ql-editor'); this.root.classList.add('ql-blank'); this.root.setAttribute('data-gramm', false); @@ -76,28 +78,34 @@ class Quill { this.emitter = new Emitter(); this.scroll = Parchment.create(this.root, { emitter: this.emitter, - whitelist: this.options.formats + whitelist: this.options.formats, }); this.editor = new Editor(this.scroll); this.selection = new Selection(this.scroll, this.emitter); - this.theme = new this.options.theme(this, this.options); + this.theme = new this.options.theme(this, this.options); // eslint-disable-line new-cap this.keyboard = this.theme.addModule('keyboard'); this.clipboard = this.theme.addModule('clipboard'); this.history = this.theme.addModule('history'); this.theme.init(); - this.emitter.on(Emitter.events.EDITOR_CHANGE, (type) => { + this.emitter.on(Emitter.events.EDITOR_CHANGE, type => { if (type === Emitter.events.TEXT_CHANGE) { this.root.classList.toggle('ql-blank', this.editor.isBlank()); } }); this.emitter.on(Emitter.events.SCROLL_UPDATE, (source, mutations) => { - let range = this.selection.lastRange; - let index = range && range.length === 0 ? range.index : undefined; - modify.call(this, () => { - return this.editor.update(null, mutations, index); - }, source); + const range = this.selection.lastRange; + const index = range && range.length === 0 ? range.index : undefined; + modify.call( + this, + () => { + return this.editor.update(null, mutations, index); + }, + source, + ); }); - let contents = this.clipboard.convert(`
${html}


`); + const contents = this.clipboard.convert( + `
${html}


`, + ); this.setContents(contents); this.history.clear(); if (this.options.placeholder) { @@ -110,7 +118,7 @@ class Quill { addContainer(container, refNode = null) { if (typeof container === 'string') { - let className = container; + const className = container; container = document.createElement('div'); container.classList.add(className); } @@ -124,9 +132,15 @@ class Quill { deleteText(index, length, source) { [index, length, , source] = overload(index, length, source); - return modify.call(this, () => { - return this.editor.deleteText(index, length); - }, source, index, -1*length); + return modify.call( + this, + () => { + return this.editor.deleteText(index, length); + }, + source, + index, + -1 * length, + ); } disable() { @@ -139,45 +153,79 @@ class Quill { } focus() { - let scrollTop = this.scrollingContainer.scrollTop; + const { scrollTop } = this.scrollingContainer; this.selection.focus(); this.scrollingContainer.scrollTop = scrollTop; this.scrollIntoView(); } format(name, value, source = Emitter.sources.API) { - return modify.call(this, () => { - let range = this.getSelection(true); - let change = new Delta(); - if (range == null) { + return modify.call( + this, + () => { + const range = this.getSelection(true); + let change = new Delta(); + if (range == null) { + return change; + } else if (Parchment.query(name, Parchment.Scope.BLOCK)) { + change = this.editor.formatLine(range.index, range.length, { + [name]: value, + }); + } else if (range.length === 0) { + this.selection.format(name, value); + return change; + } else { + change = this.editor.formatText(range.index, range.length, { + [name]: value, + }); + } + this.setSelection(range, Emitter.sources.SILENT); return change; - } else if (Parchment.query(name, Parchment.Scope.BLOCK)) { - change = this.editor.formatLine(range.index, range.length, { [name]: value }); - } else if (range.length === 0) { - this.selection.format(name, value); - return change; - } else { - change = this.editor.formatText(range.index, range.length, { [name]: value }); - } - this.setSelection(range, Emitter.sources.SILENT); - return change; - }, source); + }, + source, + ); } formatLine(index, length, name, value, source) { let formats; - [index, length, formats, source] = overload(index, length, name, value, source); - return modify.call(this, () => { - return this.editor.formatLine(index, length, formats); - }, source, index, 0); + // eslint-disable-next-line prefer-const + [index, length, formats, source] = overload( + index, + length, + name, + value, + source, + ); + return modify.call( + this, + () => { + return this.editor.formatLine(index, length, formats); + }, + source, + index, + 0, + ); } formatText(index, length, name, value, source) { let formats; - [index, length, formats, source] = overload(index, length, name, value, source); - return modify.call(this, () => { - return this.editor.formatText(index, length, formats); - }, source, index, 0); + // eslint-disable-next-line prefer-const + [index, length, formats, source] = overload( + index, + length, + name, + value, + source, + ); + return modify.call( + this, + () => { + return this.editor.formatText(index, length, formats); + }, + source, + index, + 0, + ); } getBounds(index, length = 0) { @@ -187,14 +235,14 @@ class Quill { } else { bounds = this.selection.getBounds(index.index, index.length); } - let containerBounds = this.container.getBoundingClientRect(); + const containerBounds = this.container.getBoundingClientRect(); return { bottom: bounds.bottom - containerBounds.top, height: bounds.height, left: bounds.left - containerBounds.left, right: bounds.right - containerBounds.left, top: bounds.top - containerBounds.top, - width: bounds.width + width: bounds.width, }; } @@ -206,9 +254,8 @@ class Quill { getFormat(index = this.getSelection(true), length = 0) { if (typeof index === 'number') { return this.editor.getFormat(index, length); - } else { - return this.editor.getFormat(index.index, index.length); } + return this.editor.getFormat(index.index, index.length); } getIndex(blot) { @@ -230,9 +277,8 @@ class Quill { getLines(index = 0, length = Number.MAX_VALUE) { if (typeof index !== 'number') { return this.scroll.lines(index.index, index.length); - } else { - return this.scroll.lines(index, length); } + return this.scroll.lines(index, length); } getModule(name) { @@ -241,7 +287,7 @@ class Quill { getSelection(focus = false) { if (focus) this.focus(); - this.update(); // Make sure we access getRange with editor in consistent state + this.update(); // Make sure we access getRange with editor in consistent state return this.selection.getRange()[0]; } @@ -255,40 +301,57 @@ class Quill { } insertEmbed(index, embed, value, source = Quill.sources.API) { - return modify.call(this, () => { - return this.editor.insertEmbed(index, embed, value); - }, source, index); + return modify.call( + this, + () => { + return this.editor.insertEmbed(index, embed, value); + }, + source, + index, + ); } insertText(index, text, name, value, source) { let formats; + // eslint-disable-next-line prefer-const [index, , formats, source] = overload(index, 0, name, value, source); - return modify.call(this, () => { - return this.editor.insertText(index, text, formats); - }, source, index, text.length); + return modify.call( + this, + () => { + return this.editor.insertText(index, text, formats); + }, + source, + index, + text.length, + ); } isEnabled() { return !this.container.classList.contains('ql-disabled'); } - off() { - return this.emitter.off.apply(this.emitter, arguments); + off(...args) { + return this.emitter.off(...args); } - on() { - return this.emitter.on.apply(this.emitter, arguments); + on(...args) { + return this.emitter.on(...args); } - once() { - return this.emitter.once.apply(this.emitter, arguments); + once(...args) { + return this.emitter.once(...args); } removeFormat(index, length, source) { [index, length, , source] = overload(index, length, source); - return modify.call(this, () => { - return this.editor.removeFormat(index, length); - }, source, index); + return modify.call( + this, + () => { + return this.editor.removeFormat(index, length); + }, + source, + index, + ); } scrollIntoView() { @@ -296,19 +359,26 @@ class Quill { } setContents(delta, source = Emitter.sources.API) { - return modify.call(this, () => { - delta = new Delta(delta); - let length = this.getLength(); - let deleted = this.editor.deleteText(0, length); - let applied = this.editor.applyDelta(delta); - let lastOp = applied.ops[applied.ops.length - 1]; - if (lastOp != null && typeof(lastOp.insert) === 'string' && lastOp.insert[lastOp.insert.length-1] === '\n') { - this.editor.deleteText(this.getLength() - 1, 1); - applied.delete(1); - } - let ret = deleted.compose(applied); - return ret; - }, source); + return modify.call( + this, + () => { + delta = new Delta(delta); + const length = this.getLength(); + const deleted = this.editor.deleteText(0, length); + const applied = this.editor.applyDelta(delta); + const lastOp = applied.ops[applied.ops.length - 1]; + if ( + lastOp != null && + typeof lastOp.insert === 'string' && + lastOp.insert[lastOp.insert.length - 1] === '\n' + ) { + this.editor.deleteText(this.getLength() - 1, 1); + applied.delete(1); + } + return deleted.compose(applied); + }, + source, + ); } setSelection(index, length, source) { @@ -324,21 +394,26 @@ class Quill { } setText(text, source = Emitter.sources.API) { - let delta = new Delta().insert(text); + const delta = new Delta().insert(text); return this.setContents(delta, source); } update(source = Emitter.sources.USER) { - let change = this.scroll.update(source); // Will update selection before selection.update() does if text changes + const change = this.scroll.update(source); // Will update selection before selection.update() does if text changes this.selection.update(source); return change; } updateContents(delta, source = Emitter.sources.API) { - return modify.call(this, () => { - delta = new Delta(delta); - return this.editor.applyDelta(delta, source); - }, source, true); + return modify.call( + this, + () => { + delta = new Delta(delta); + return this.editor.applyDelta(delta, source); + }, + source, + true, + ); } } Quill.DEFAULTS = { @@ -348,71 +423,92 @@ Quill.DEFAULTS = { placeholder: '', readOnly: false, scrollingContainer: null, - theme: 'default' + theme: 'default', }; Quill.events = Emitter.events; Quill.sources = Emitter.sources; // eslint-disable-next-line no-undef -Quill.version = typeof(QUILL_VERSION) === 'undefined' ? 'dev' : QUILL_VERSION; +Quill.version = typeof QUILL_VERSION === 'undefined' ? 'dev' : QUILL_VERSION; Quill.imports = { - 'delta' : Delta, - 'parchment' : Parchment, - 'core/module' : Module, - 'core/theme' : Theme + delta: Delta, + parchment: Parchment, + 'core/module': Module, + 'core/theme': Theme, }; - function expandConfig(container, userConfig) { - userConfig = extend(true, { - container: container, - modules: { - clipboard: true, - keyboard: true, - history: true - } - }, userConfig); + userConfig = extend( + true, + { + container, + modules: { + clipboard: true, + keyboard: true, + history: true, + }, + }, + userConfig, + ); if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) { userConfig.theme = Theme; } else { userConfig.theme = Quill.import(`themes/${userConfig.theme}`); if (userConfig.theme == null) { - throw new Error(`Invalid theme ${userConfig.theme}. Did you register it?`); + throw new Error( + `Invalid theme ${userConfig.theme}. Did you register it?`, + ); } } - let themeConfig = extend(true, {}, userConfig.theme.DEFAULTS); - [themeConfig, userConfig].forEach(function(config) { + const themeConfig = extend(true, {}, userConfig.theme.DEFAULTS); + [themeConfig, userConfig].forEach(config => { config.modules = config.modules || {}; - Object.keys(config.modules).forEach(function(module) { + Object.keys(config.modules).forEach(module => { if (config.modules[module] === true) { config.modules[module] = {}; } }); }); - let moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules)); - let moduleConfig = moduleNames.reduce(function(config, name) { - let moduleClass = Quill.import(`modules/${name}`); + const moduleNames = Object.keys(themeConfig.modules).concat( + Object.keys(userConfig.modules), + ); + const moduleConfig = moduleNames.reduce((config, name) => { + const moduleClass = Quill.import(`modules/${name}`); if (moduleClass == null) { - debug.error(`Cannot load ${name} module. Are you sure you registered it?`); + debug.error( + `Cannot load ${name} module. Are you sure you registered it?`, + ); } else { config[name] = moduleClass.DEFAULTS || {}; } return config; }, {}); // Special case toolbar shorthand - if (userConfig.modules != null && userConfig.modules.toolbar && - userConfig.modules.toolbar.constructor !== Object) { + if ( + userConfig.modules != null && + userConfig.modules.toolbar && + userConfig.modules.toolbar.constructor !== Object + ) { userConfig.modules.toolbar = { - container: userConfig.modules.toolbar + container: userConfig.modules.toolbar, }; } - userConfig = extend(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig); - ['bounds', 'container', 'scrollingContainer'].forEach(function(key) { + userConfig = extend( + true, + {}, + Quill.DEFAULTS, + { modules: moduleConfig }, + themeConfig, + userConfig, + ); + ['bounds', 'container', 'scrollingContainer'].forEach(key => { if (typeof userConfig[key] === 'string') { userConfig[key] = document.querySelector(userConfig[key]); } }); - userConfig.modules = Object.keys(userConfig.modules).reduce(function(config, name) { + userConfig.modules = Object.keys( + userConfig.modules, + ).reduce((config, name) => { if (userConfig.modules[name]) { config[name] = userConfig.modules[name]; } @@ -428,10 +524,12 @@ function modify(modifier, source, index, shift) { return new Delta(); } let range = index == null ? null : this.getSelection(); - let oldDelta = this.editor.delta; - let change = modifier(); + const oldDelta = this.editor.delta; + const change = modifier(); if (range != null) { - if (index === true) index = range.index; + if (index === true) { + index = range.index; // eslint-disable-line prefer-destructuring + } if (shift == null) { range = shiftRange(range, change, source); } else if (shift !== 0) { @@ -440,7 +538,7 @@ function modify(modifier, source, index, shift) { this.setSelection(range, Emitter.sources.SILENT); } if (change.length() > 0) { - let args = [Emitter.events.TEXT_CHANGE, change, oldDelta, source]; + const args = [Emitter.events.TEXT_CHANGE, change, oldDelta, source]; this.emitter.emit(Emitter.events.EDITOR_CHANGE, ...args); if (source !== Emitter.sources.SILENT) { this.emitter.emit(...args); @@ -454,12 +552,20 @@ function overload(index, length, name, value, source) { if (typeof index.index === 'number' && typeof index.length === 'number') { // Allow for throwaway end (used by insertText/insertEmbed) if (typeof length !== 'number') { - source = value, value = name, name = length, length = index.length, index = index.index; + source = value; + value = name; + name = length; + length = index.length; // eslint-disable-line prefer-destructuring + index = index.index; // eslint-disable-line prefer-destructuring } else { - length = index.length, index = index.index; + length = index.length; // eslint-disable-line prefer-destructuring + index = index.index; // eslint-disable-line prefer-destructuring } } else if (typeof length !== 'number') { - source = value, value = name, name = length, length = 0; + source = value; + value = name; + name = length; + length = 0; } // Handle format being object, two format name/value strings or excluded if (typeof name === 'object') { @@ -479,23 +585,23 @@ function overload(index, length, name, value, source) { function shiftRange(range, index, length, source) { if (range == null) return null; - let start, end; + let start; + let end; if (index instanceof Delta) { - [start, end] = [range.index, range.index + range.length].map(function(pos) { - return index.transformPosition(pos, source !== Emitter.sources.USER); - }); + [start, end] = [range.index, range.index + range.length].map(pos => + index.transformPosition(pos, source !== Emitter.sources.USER), + ); } else { - [start, end] = [range.index, range.index + range.length].map(function(pos) { - if (pos < index || (pos === index && source === Emitter.sources.USER)) return pos; + [start, end] = [range.index, range.index + range.length].map(pos => { + if (pos < index || (pos === index && source === Emitter.sources.USER)) + return pos; if (length >= 0) { return pos + length; - } else { - return Math.max(index, pos + length); } + return Math.max(index, pos + length); }); } return new Range(start, end - start); } - export { expandConfig, overload, Quill as default }; diff --git a/core/selection.js b/core/selection.js index 92131a74bf..8a8fb9547b 100644 --- a/core/selection.js +++ b/core/selection.js @@ -4,8 +4,7 @@ import equal from 'deep-equal'; import Emitter from './emitter'; import logger from './logger'; -let debug = logger('quill:selection'); - +const debug = logger('quill:selection'); class Range { constructor(index, length = 0) { @@ -14,7 +13,6 @@ class Range { } } - class Selection { constructor(scroll, emitter) { this.emitter = emitter; @@ -24,7 +22,8 @@ class Selection { this.root = this.scroll.domNode; this.cursor = Parchment.create('cursor', this); // savedRange is last non-null range - this.lastRange = this.savedRange = new Range(0, 0); + this.savedRange = new Range(0, 0); + this.lastRange = this.savedRange; this.handleComposition(); this.handleDragging(); this.emitter.listenDOM('selectionchange', document, () => { @@ -39,14 +38,21 @@ class Selection { }); this.emitter.on(Emitter.events.SCROLL_BEFORE_UPDATE, () => { if (!this.hasFocus()) return; - let native = this.getNativeRange(); + const native = this.getNativeRange(); if (native == null) return; - if (native.start.node === this.cursor.textNode) return; // cursor.restore() will handle + if (native.start.node === this.cursor.textNode) return; // cursor.restore() will handle // TODO unclear if this has negative side effects this.emitter.once(Emitter.events.SCROLL_UPDATE, () => { try { - this.setNativeRange(native.start.node, native.start.offset, native.end.node, native.end.offset); - } catch (ignored) {} + this.setNativeRange( + native.start.node, + native.start.offset, + native.end.node, + native.end.offset, + ); + } catch (ignored) { + // ignore + } }); }); this.emitter.on(Emitter.events.SCROLL_OPTIMIZE, (mutations, context) => { @@ -68,7 +74,12 @@ class Selection { const range = this.cursor.restore(); if (!range) return; setTimeout(() => { - this.setNativeRange(range.startNode, range.startOffset, range.endNode, range.endOffset); + this.setNativeRange( + range.startNode, + range.startOffset, + range.endNode, + range.endOffset, + ); }, 1); } }); @@ -93,17 +104,22 @@ class Selection { format(format, value) { if (this.scroll.whitelist != null && !this.scroll.whitelist[format]) return; this.scroll.update(); - let nativeRange = this.getNativeRange(); - if (nativeRange == null || !nativeRange.native.collapsed || Parchment.query(format, Parchment.Scope.BLOCK)) return; + const nativeRange = this.getNativeRange(); + if ( + nativeRange == null || + !nativeRange.native.collapsed || + Parchment.query(format, Parchment.Scope.BLOCK) + ) + return; if (nativeRange.start.node !== this.cursor.textNode) { - let blot = Parchment.find(nativeRange.start.node, false); + const blot = Parchment.find(nativeRange.start.node, false); if (blot == null) return; // TODO Give blot ability to not split if (blot instanceof Parchment.Leaf) { - let after = blot.split(nativeRange.start.offset); + const after = blot.split(nativeRange.start.offset); blot.parent.insertBefore(this.cursor, after); } else { - blot.insertBefore(this.cursor, nativeRange.start.node); // Should never happen + blot.insertBefore(this.cursor, nativeRange.start.node); // Should never happen } this.cursor.attach(); } @@ -114,13 +130,14 @@ class Selection { } getBounds(index, length = 0) { - let scrollLength = this.scroll.length(); + const scrollLength = this.scroll.length(); index = Math.min(index, scrollLength - 1); length = Math.min(index + length, scrollLength - 1) - index; - let node, [leaf, offset] = this.scroll.leaf(index); + let node; + let [leaf, offset] = this.scroll.leaf(index); if (leaf == null) return null; [node, offset] = leaf.position(offset, true); - let range = document.createRange(); + const range = document.createRange(); if (length > 0) { range.setStart(node, offset); [leaf, offset] = this.scroll.leaf(index + length); @@ -128,48 +145,47 @@ class Selection { [node, offset] = leaf.position(offset, true); range.setEnd(node, offset); return range.getBoundingClientRect(); - } else { - let side = 'left'; - let rect; - if (node instanceof Text) { - if (offset < node.data.length) { - range.setStart(node, offset); - range.setEnd(node, offset + 1); - } else { - range.setStart(node, offset - 1); - range.setEnd(node, offset); - side = 'right'; - } - rect = range.getBoundingClientRect(); + } + let side = 'left'; + let rect; + if (node instanceof Text) { + if (offset < node.data.length) { + range.setStart(node, offset); + range.setEnd(node, offset + 1); } else { - rect = leaf.domNode.getBoundingClientRect(); - if (offset > 0) side = 'right'; + range.setStart(node, offset - 1); + range.setEnd(node, offset); + side = 'right'; } - return { - bottom: rect.top + rect.height, - height: rect.height, - left: rect[side], - right: rect[side], - top: rect.top, - width: 0 - }; + rect = range.getBoundingClientRect(); + } else { + rect = leaf.domNode.getBoundingClientRect(); + if (offset > 0) side = 'right'; } + return { + bottom: rect.top + rect.height, + height: rect.height, + left: rect[side], + right: rect[side], + top: rect.top, + width: 0, + }; } getNativeRange() { - let selection = document.getSelection(); + const selection = document.getSelection(); if (selection == null || selection.rangeCount <= 0) return null; - let nativeRange = selection.getRangeAt(0); + const nativeRange = selection.getRangeAt(0); if (nativeRange == null) return null; - let range = this.normalizeNative(nativeRange); + const range = this.normalizeNative(nativeRange); debug.info('getNativeRange', range); return range; } getRange() { - let normalized = this.getNativeRange(); + const normalized = this.getNativeRange(); if (normalized == null) return [null, null]; - let range = this.normalizedToRange(normalized); + const range = this.normalizedToRange(normalized); return [range, normalized]; } @@ -178,116 +194,142 @@ class Selection { } normalizedToRange(range) { - let positions = [[range.start.node, range.start.offset]]; + const positions = [[range.start.node, range.start.offset]]; if (!range.native.collapsed) { positions.push([range.end.node, range.end.offset]); } - let indexes = positions.map((position) => { - let [node, offset] = position; - let blot = Parchment.find(node, true); - let index = blot.offset(this.scroll); + const indexes = positions.map(position => { + const [node, offset] = position; + const blot = Parchment.find(node, true); + const index = blot.offset(this.scroll); if (offset === 0) { return index; } else if (blot instanceof Parchment.Container) { return index + blot.length(); - } else { - return index + blot.index(node, offset); } + return index + blot.index(node, offset); }); - let end = Math.min(Math.max(...indexes), this.scroll.length() - 1); - let start = Math.min(end, ...indexes); - return new Range(start, end-start); + const end = Math.min(Math.max(...indexes), this.scroll.length() - 1); + const start = Math.min(end, ...indexes); + return new Range(start, end - start); } normalizeNative(nativeRange) { - if (!contains(this.root, nativeRange.startContainer) || - (!nativeRange.collapsed && !contains(this.root, nativeRange.endContainer))) { + if ( + !contains(this.root, nativeRange.startContainer) || + (!nativeRange.collapsed && !contains(this.root, nativeRange.endContainer)) + ) { return null; } - let range = { - start: { node: nativeRange.startContainer, offset: nativeRange.startOffset }, + const range = { + start: { + node: nativeRange.startContainer, + offset: nativeRange.startOffset, + }, end: { node: nativeRange.endContainer, offset: nativeRange.endOffset }, - native: nativeRange + native: nativeRange, }; - [range.start, range.end].forEach(function(position) { - let node = position.node, offset = position.offset; + [range.start, range.end].forEach(position => { + let { node, offset } = position; while (!(node instanceof Text) && node.childNodes.length > 0) { if (node.childNodes.length > offset) { node = node.childNodes[offset]; offset = 0; } else if (node.childNodes.length === offset) { node = node.lastChild; - offset = node instanceof Text ? node.data.length : node.childNodes.length + 1; + offset = + node instanceof Text + ? node.data.length + : node.childNodes.length + 1; } else { break; } } - position.node = node, position.offset = offset; + position.node = node; + position.offset = offset; }); return range; } rangeToNative(range) { - let indexes = range.collapsed ? [range.index] : [range.index, range.index + range.length]; - let args = []; - let scrollLength = this.scroll.length(); + const indexes = range.collapsed + ? [range.index] + : [range.index, range.index + range.length]; + const args = []; + const scrollLength = this.scroll.length(); indexes.forEach((index, i) => { index = Math.min(scrollLength - 1, index); - let node, [leaf, offset] = this.scroll.leaf(index); - [node, offset] = leaf.position(offset, i !== 0); + const [leaf, leafOffset] = this.scroll.leaf(index); + const [node, offset] = leaf.position(leafOffset, i !== 0); args.push(node, offset); }); if (args.length < 2) { - args = args.concat(args); + return args.concat(args); } return args; } scrollIntoView(scrollingContainer) { - let range = this.lastRange; + const range = this.lastRange; if (range == null) return; - let bounds = this.getBounds(range.index, range.length); + const bounds = this.getBounds(range.index, range.length); if (bounds == null) return; - let limit = this.scroll.length()-1; - let [first, ] = this.scroll.line(Math.min(range.index, limit)); + const limit = this.scroll.length() - 1; + const [first] = this.scroll.line(Math.min(range.index, limit)); let last = first; if (range.length > 0) { - [last, ] = this.scroll.line(Math.min(range.index + range.length, limit)); + [last] = this.scroll.line(Math.min(range.index + range.length, limit)); } if (first == null || last == null) return; - let scrollBounds = scrollingContainer.getBoundingClientRect(); + const scrollBounds = scrollingContainer.getBoundingClientRect(); if (bounds.top < scrollBounds.top) { - scrollingContainer.scrollTop -= (scrollBounds.top - bounds.top); + scrollingContainer.scrollTop -= scrollBounds.top - bounds.top; } else if (bounds.bottom > scrollBounds.bottom) { - scrollingContainer.scrollTop += (bounds.bottom - scrollBounds.bottom); + scrollingContainer.scrollTop += bounds.bottom - scrollBounds.bottom; } } - setNativeRange(startNode, startOffset, endNode = startNode, endOffset = startOffset, force = false) { + setNativeRange( + startNode, + startOffset, + endNode = startNode, + endOffset = startOffset, + force = false, + ) { debug.info('setNativeRange', startNode, startOffset, endNode, endOffset); - if (startNode != null && (this.root.parentNode == null || startNode.parentNode == null || endNode.parentNode == null)) { + if ( + startNode != null && + (this.root.parentNode == null || + startNode.parentNode == null || + endNode.parentNode == null) + ) { return; } - let selection = document.getSelection(); + const selection = document.getSelection(); if (selection == null) return; if (startNode != null) { if (!this.hasFocus()) this.root.focus(); - let native = (this.getNativeRange() || {}).native; - if (native == null || force || - startNode !== native.startContainer || - startOffset !== native.startOffset || - endNode !== native.endContainer || - endOffset !== native.endOffset) { - - if (startNode.tagName == "BR") { - startOffset = [].indexOf.call(startNode.parentNode.childNodes, startNode); + const { native } = this.getNativeRange() || {}; + if ( + native == null || + force || + startNode !== native.startContainer || + startOffset !== native.startOffset || + endNode !== native.endContainer || + endOffset !== native.endOffset + ) { + if (startNode.tagName === 'BR') { + startOffset = [].indexOf.call( + startNode.parentNode.childNodes, + startNode, + ); startNode = startNode.parentNode; } - if (endNode.tagName == "BR") { + if (endNode.tagName === 'BR') { endOffset = [].indexOf.call(endNode.parentNode.childNodes, endNode); endNode = endNode.parentNode; } - let range = document.createRange(); + const range = document.createRange(); range.setStart(startNode, startOffset); range.setEnd(endNode, endOffset); selection.removeAllRanges(); @@ -296,7 +338,7 @@ class Selection { } else { selection.removeAllRanges(); this.root.blur(); - document.body.focus(); // root.blur() not enough on IE11+Travis+SauceLabs (but not local VMs) + document.body.focus(); // root.blur() not enough on IE11+Travis+SauceLabs (but not local VMs) } } @@ -307,7 +349,7 @@ class Selection { } debug.info('setRange', range); if (range != null) { - let args = this.rangeToNative(range); + const args = this.rangeToNative(range); this.setNativeRange(...args, force); } else { this.setNativeRange(null); @@ -316,17 +358,27 @@ class Selection { } update(source = Emitter.sources.USER) { - let oldRange = this.lastRange; - let [lastRange, nativeRange] = this.getRange(); + const oldRange = this.lastRange; + const [lastRange, nativeRange] = this.getRange(); this.lastRange = lastRange; if (this.lastRange != null) { this.savedRange = this.lastRange; } if (!equal(oldRange, this.lastRange)) { - if (!this.composing && nativeRange != null && nativeRange.native.collapsed && nativeRange.start.node !== this.cursor.textNode) { + if ( + !this.composing && + nativeRange != null && + nativeRange.native.collapsed && + nativeRange.start.node !== this.cursor.textNode + ) { this.cursor.restore(); } - let args = [Emitter.events.SELECTION_CHANGE, clone(this.lastRange), clone(oldRange), source]; + const args = [ + Emitter.events.SELECTION_CHANGE, + clone(this.lastRange), + clone(oldRange), + source, + ]; this.emitter.emit(Emitter.events.EDITOR_CHANGE, ...args); if (source !== Emitter.sources.SILENT) { this.emitter.emit(...args); @@ -335,21 +387,19 @@ class Selection { } } - function contains(parent, descendant) { try { // Firefox inserts inaccessible nodes around video elements - descendant.parentNode; + descendant.parentNode; // eslint-disable-line no-unused-expressions } catch (e) { return false; } // IE11 has bug with Text nodes // https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect if (descendant instanceof Text) { - descendant = descendant.parentNode; + parent.contains(descendant.parentNode); } return parent.contains(descendant); } - export { Range, Selection as default }; diff --git a/core/theme.js b/core/theme.js index 3e9103eba5..85374df667 100644 --- a/core/theme.js +++ b/core/theme.js @@ -6,7 +6,7 @@ class Theme { } init() { - Object.keys(this.options.modules).forEach((name) => { + Object.keys(this.options.modules).forEach(name => { if (this.modules[name] == null) { this.addModule(name); } @@ -14,17 +14,19 @@ class Theme { } addModule(name) { - let moduleClass = this.quill.constructor.import(`modules/${name}`); - this.modules[name] = new moduleClass(this.quill, this.options.modules[name] || {}); + const ModuleClass = this.quill.constructor.import(`modules/${name}`); + this.modules[name] = new ModuleClass( + this.quill, + this.options.modules[name] || {}, + ); return this.modules[name]; } } Theme.DEFAULTS = { - modules: {} + modules: {}, }; Theme.themes = { - 'default': Theme + default: Theme, }; - export default Theme; diff --git a/formats/align.js b/formats/align.js index 2e6db385be..6160943b4f 100644 --- a/formats/align.js +++ b/formats/align.js @@ -1,12 +1,20 @@ import Parchment from 'parchment'; -let config = { +const config = { scope: Parchment.Scope.BLOCK, - whitelist: ['right', 'center', 'justify'] + whitelist: ['right', 'center', 'justify'], }; -let AlignAttribute = new Parchment.Attributor.Attribute('align', 'align', config); -let AlignClass = new Parchment.Attributor.Class('align', 'ql-align', config); -let AlignStyle = new Parchment.Attributor.Style('align', 'text-align', config); +const AlignAttribute = new Parchment.Attributor.Attribute( + 'align', + 'align', + config, +); +const AlignClass = new Parchment.Attributor.Class('align', 'ql-align', config); +const AlignStyle = new Parchment.Attributor.Style( + 'align', + 'text-align', + config, +); export { AlignAttribute, AlignClass, AlignStyle }; diff --git a/formats/background.js b/formats/background.js index 3b48226c01..475521e4b0 100644 --- a/formats/background.js +++ b/formats/background.js @@ -1,11 +1,11 @@ import Parchment from 'parchment'; import { ColorAttributor } from './color'; -let BackgroundClass = new Parchment.Attributor.Class('background', 'ql-bg', { - scope: Parchment.Scope.INLINE +const BackgroundClass = new Parchment.Attributor.Class('background', 'ql-bg', { + scope: Parchment.Scope.INLINE, }); -let BackgroundStyle = new ColorAttributor('background', 'background-color', { - scope: Parchment.Scope.INLINE +const BackgroundStyle = new ColorAttributor('background', 'background-color', { + scope: Parchment.Scope.INLINE, }); export { BackgroundClass, BackgroundStyle }; diff --git a/formats/blockquote.js b/formats/blockquote.js index baffb44b4c..4a4111c915 100644 --- a/formats/blockquote.js +++ b/formats/blockquote.js @@ -1,9 +1,7 @@ import Block from '../blots/block'; - class Blockquote extends Block {} Blockquote.blotName = 'blockquote'; Blockquote.tagName = 'blockquote'; - export default Blockquote; diff --git a/formats/code.js b/formats/code.js index fff023589f..63b09b9d7a 100644 --- a/formats/code.js +++ b/formats/code.js @@ -4,15 +4,13 @@ import Block from '../blots/block'; import Inline from '../blots/inline'; import TextBlot from '../blots/text'; - class Code extends Inline {} Code.blotName = 'code'; Code.tagName = 'CODE'; - class CodeBlock extends Block { static create(value) { - let domNode = super.create(value); + const domNode = super.create(value); domNode.setAttribute('spellcheck', false); return domNode; } @@ -23,7 +21,8 @@ class CodeBlock extends Block { delta() { let text = this.domNode.textContent; - if (text.endsWith('\n')) { // Should always be true + if (text.endsWith('\n')) { + // Should always be true text = text.slice(0, -1); } return text.split('\n').reduce((delta, frag) => { @@ -33,7 +32,7 @@ class CodeBlock extends Block { format(name, value) { if (name === this.statics.blotName && value) return; - let [text, ] = this.descendant(TextBlot, this.length() - 1); + const [text] = this.descendant(TextBlot, this.length() - 1); if (text != null) { text.deleteAt(text.length() - 1, 1); } @@ -42,30 +41,38 @@ class CodeBlock extends Block { formatAt(index, length, name, value) { if (length === 0) return; - if (Parchment.query(name, Parchment.Scope.BLOCK) == null || - (name === this.statics.blotName && value === this.statics.formats(this.domNode))) { + if ( + Parchment.query(name, Parchment.Scope.BLOCK) == null || + (name === this.statics.blotName && + value === this.statics.formats(this.domNode)) + ) { return; } - let nextNewline = this.newlineIndex(index); + const nextNewline = this.newlineIndex(index); if (nextNewline < 0 || nextNewline >= index + length) return; - let prevNewline = this.newlineIndex(index, true) + 1; - let isolateLength = nextNewline - prevNewline + 1; - let blot = this.isolate(prevNewline, isolateLength); - let next = blot.next; + const prevNewline = this.newlineIndex(index, true) + 1; + const isolateLength = nextNewline - prevNewline + 1; + const blot = this.isolate(prevNewline, isolateLength); + const { next } = blot; blot.format(name, value); if (next instanceof CodeBlock) { - next.formatAt(0, index - prevNewline + length - isolateLength, name, value); + next.formatAt( + 0, + index - prevNewline + length - isolateLength, + name, + value, + ); } } insertAt(index, value, def) { if (def != null) return; - let [text, offset] = this.descendant(TextBlot, index); + const [text, offset] = this.descendant(TextBlot, index); text.insertAt(offset, value); } length() { - let length = this.domNode.textContent.length; + const { length } = this.domNode.textContent; if (!this.domNode.textContent.endsWith('\n')) { return length + 1; } @@ -74,11 +81,10 @@ class CodeBlock extends Block { newlineIndex(searchIndex, reverse = false) { if (!reverse) { - let offset = this.domNode.textContent.slice(searchIndex).indexOf('\n'); + const offset = this.domNode.textContent.slice(searchIndex).indexOf('\n'); return offset > -1 ? searchIndex + offset : -1; - } else { - return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\n'); } + return this.domNode.textContent.slice(0, searchIndex).lastIndexOf('\n'); } optimize(context) { @@ -86,10 +92,13 @@ class CodeBlock extends Block { this.appendChild(Parchment.create('text', '\n')); } super.optimize(context); - let next = this.next; - if (next != null && next.prev === this && - next.statics.blotName === this.statics.blotName && - this.statics.formats(this.domNode) === next.statics.formats(next.domNode)) { + const { next } = this; + if ( + next != null && + next.prev === this && + next.statics.blotName === this.statics.blotName && + this.statics.formats(this.domNode) === next.statics.formats(next.domNode) + ) { next.optimize(context); next.moveChildren(this); next.remove(); @@ -98,8 +107,8 @@ class CodeBlock extends Block { replace(target) { super.replace(target); - [].slice.call(this.domNode.querySelectorAll('*')).forEach(function(node) { - let blot = Parchment.find(node); + [].slice.call(this.domNode.querySelectorAll('*')).forEach(node => { + const blot = Parchment.find(node); if (blot == null) { node.parentNode.removeChild(node); } else if (blot instanceof Parchment.Embed) { @@ -114,5 +123,4 @@ CodeBlock.blotName = 'code-block'; CodeBlock.tagName = 'PRE'; CodeBlock.TAB = ' '; - export { Code, CodeBlock as default }; diff --git a/formats/color.js b/formats/color.js index a431f041db..4ec42dab67 100644 --- a/formats/color.js +++ b/formats/color.js @@ -5,17 +5,19 @@ class ColorAttributor extends Parchment.Attributor.Style { let value = super.value(domNode); if (!value.startsWith('rgb(')) return value; value = value.replace(/^[^\d]+/, '').replace(/[^\d]+$/, ''); - return '#' + value.split(',').map(function(component) { - return ('00' + parseInt(component).toString(16)).slice(-2); - }).join(''); + const hex = value + .split(',') + .map(component => `00${parseInt(component, 10).toString(16)}`.slice(-2)) + .join(''); + return `#${hex}`; } } -let ColorClass = new Parchment.Attributor.Class('color', 'ql-color', { - scope: Parchment.Scope.INLINE +const ColorClass = new Parchment.Attributor.Class('color', 'ql-color', { + scope: Parchment.Scope.INLINE, }); -let ColorStyle = new ColorAttributor('color', 'color', { - scope: Parchment.Scope.INLINE +const ColorStyle = new ColorAttributor('color', 'color', { + scope: Parchment.Scope.INLINE, }); export { ColorAttributor, ColorClass, ColorStyle }; diff --git a/formats/direction.js b/formats/direction.js index b4b05d6a40..74a3c0f166 100644 --- a/formats/direction.js +++ b/formats/direction.js @@ -1,12 +1,24 @@ import Parchment from 'parchment'; -let config = { +const config = { scope: Parchment.Scope.BLOCK, - whitelist: ['rtl'] + whitelist: ['rtl'], }; -let DirectionAttribute = new Parchment.Attributor.Attribute('direction', 'dir', config); -let DirectionClass = new Parchment.Attributor.Class('direction', 'ql-direction', config); -let DirectionStyle = new Parchment.Attributor.Style('direction', 'direction', config); +const DirectionAttribute = new Parchment.Attributor.Attribute( + 'direction', + 'dir', + config, +); +const DirectionClass = new Parchment.Attributor.Class( + 'direction', + 'ql-direction', + config, +); +const DirectionStyle = new Parchment.Attributor.Style( + 'direction', + 'direction', + config, +); export { DirectionAttribute, DirectionClass, DirectionStyle }; diff --git a/formats/font.js b/formats/font.js index 888a007e98..f06ed3c7a1 100644 --- a/formats/font.js +++ b/formats/font.js @@ -1,11 +1,11 @@ import Parchment from 'parchment'; -let config = { +const config = { scope: Parchment.Scope.INLINE, - whitelist: ['serif', 'monospace'] + whitelist: ['serif', 'monospace'], }; -let FontClass = new Parchment.Attributor.Class('font', 'ql-font', config); +const FontClass = new Parchment.Attributor.Class('font', 'ql-font', config); class FontStyleAttributor extends Parchment.Attributor.Style { value(node) { @@ -13,6 +13,6 @@ class FontStyleAttributor extends Parchment.Attributor.Style { } } -let FontStyle = new FontStyleAttributor('font', 'font-family', config); +const FontStyle = new FontStyleAttributor('font', 'font-family', config); export { FontStyle, FontClass }; diff --git a/formats/formula.js b/formats/formula.js index 13c2d25e59..c17fc12a89 100644 --- a/formats/formula.js +++ b/formats/formula.js @@ -1,13 +1,12 @@ import Embed from '../blots/embed'; - class Formula extends Embed { static create(value) { - let node = super.create(value); + const node = super.create(value); if (typeof value === 'string') { window.katex.render(value, node, { throwOnError: false, - errorColor: '#f00' + errorColor: '#f00', }); node.setAttribute('data-value', value); } @@ -28,5 +27,4 @@ Formula.blotName = 'formula'; Formula.className = 'ql-formula'; Formula.tagName = 'SPAN'; - export default Formula; diff --git a/formats/header.js b/formats/header.js index 5fe39e8cb4..fe330d259c 100644 --- a/formats/header.js +++ b/formats/header.js @@ -1,6 +1,5 @@ import Block from '../blots/block'; - class Header extends Block { static formats(domNode) { return this.tagName.indexOf(domNode.tagName) + 1; @@ -9,5 +8,4 @@ class Header extends Block { Header.blotName = 'header'; Header.tagName = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6']; - export default Header; diff --git a/formats/image.js b/formats/image.js index d0c5d59e85..1c6de1a1b4 100644 --- a/formats/image.js +++ b/formats/image.js @@ -1,16 +1,11 @@ import Parchment from 'parchment'; import { sanitize } from '../formats/link'; -const ATTRIBUTES = [ - 'alt', - 'height', - 'width' -]; - +const ATTRIBUTES = ['alt', 'height', 'width']; class Image extends Parchment.Embed { static create(value) { - let node = super.create(value); + const node = super.create(value); if (typeof value === 'string') { node.setAttribute('src', this.sanitize(value)); } @@ -18,7 +13,7 @@ class Image extends Parchment.Embed { } static formats(domNode) { - return ATTRIBUTES.reduce(function(formats, attribute) { + return ATTRIBUTES.reduce((formats, attribute) => { if (domNode.hasAttribute(attribute)) { formats[attribute] = domNode.getAttribute(attribute); } @@ -32,9 +27,9 @@ class Image extends Parchment.Embed { static register() { if (/Firefox/i.test(navigator.userAgent)) { - setTimeout(function() { + setTimeout(() => { // Disable image resizing in Firefox - document.execCommand("enableObjectResizing", false, false); + document.execCommand('enableObjectResizing', false, false); }, 1); } } @@ -62,5 +57,4 @@ class Image extends Parchment.Embed { Image.blotName = 'image'; Image.tagName = 'IMG'; - export default Image; diff --git a/formats/indent.js b/formats/indent.js index b3db8a7496..467cc419a9 100644 --- a/formats/indent.js +++ b/formats/indent.js @@ -3,29 +3,28 @@ import Parchment from 'parchment'; class IdentAttributor extends Parchment.Attributor.Class { add(node, value) { if (value === '+1' || value === '-1') { - let indent = this.value(node) || 0; - value = (value === '+1' ? (indent + 1) : (indent - 1)); + const indent = this.value(node) || 0; + value = value === '+1' ? indent + 1 : indent - 1; } if (value === 0) { this.remove(node); return true; - } else { - return super.add(node, value); } + return super.add(node, value); } canAdd(node, value) { - return super.canAdd(node, value) || super.canAdd(node, parseInt(value)); + return super.canAdd(node, value) || super.canAdd(node, parseInt(value, 10)); } value(node) { - return parseInt(super.value(node)) || undefined; // Don't return NaN + return parseInt(super.value(node), 10) || undefined; // Don't return NaN } } -let IndentClass = new IdentAttributor('indent', 'ql-indent', { +const IndentClass = new IdentAttributor('indent', 'ql-indent', { scope: Parchment.Scope.BLOCK, - whitelist: [1, 2, 3, 4, 5, 6, 7, 8] + whitelist: [1, 2, 3, 4, 5, 6, 7, 8], }); -export { IndentClass }; +export default IndentClass; diff --git a/formats/italic.js b/formats/italic.js index 6d50f39f53..9ea8796fd4 100644 --- a/formats/italic.js +++ b/formats/italic.js @@ -1,6 +1,6 @@ import Bold from './bold'; -class Italic extends Bold { } +class Italic extends Bold {} Italic.blotName = 'italic'; Italic.tagName = ['EM', 'I']; diff --git a/formats/link.js b/formats/link.js index 84deebc573..323241df17 100644 --- a/formats/link.js +++ b/formats/link.js @@ -1,11 +1,9 @@ import Inline from '../blots/inline'; - class Link extends Inline { static create(value) { - let node = super.create(value); - value = this.sanitize(value); - node.setAttribute('href', value); + const node = super.create(value); + node.setAttribute('href', this.sanitize(value)); node.setAttribute('target', '_blank'); return node; } @@ -19,9 +17,11 @@ class Link extends Inline { } format(name, value) { - if (name !== this.statics.blotName || !value) return super.format(name, value); - value = this.constructor.sanitize(value); - this.domNode.setAttribute('href', value); + if (name !== this.statics.blotName || !value) { + super.format(name, value); + } else { + this.domNode.setAttribute('href', this.constructor.sanitize(value)); + } } } Link.blotName = 'link'; @@ -29,13 +29,11 @@ Link.tagName = 'A'; Link.SANITIZED_URL = 'about:blank'; Link.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel']; - function sanitize(url, protocols) { - let anchor = document.createElement('a'); + const anchor = document.createElement('a'); anchor.href = url; - let protocol = anchor.href.slice(0, anchor.href.indexOf(':')); + const protocol = anchor.href.slice(0, anchor.href.indexOf(':')); return protocols.indexOf(protocol) > -1; } - export { Link as default, sanitize }; diff --git a/formats/list.js b/formats/list.js index 03d41f8b24..43a667be6b 100644 --- a/formats/list.js +++ b/formats/list.js @@ -2,14 +2,17 @@ import Parchment from 'parchment'; import Block from '../blots/block'; import Container from '../blots/container'; +const LIST_BLOT = 'list'; class ListItem extends Block { static formats(domNode) { - return domNode.tagName === this.tagName ? undefined : super.formats(domNode); + return domNode.tagName === this.tagName + ? undefined + : super.formats(domNode); } format(name, value) { - if (name === List.blotName && !value) { + if (name === LIST_BLOT && !value) { this.replaceWith(Parchment.create(this.statics.scope)); } else { super.format(name, value); @@ -29,20 +32,18 @@ class ListItem extends Block { if (name === this.parent.statics.blotName) { this.parent.replaceWith(name, value); return this; - } else { - this.parent.unwrap(); - return super.replaceWith(name, value); } + this.parent.unwrap(); + return super.replaceWith(name, value); } } ListItem.blotName = 'list-item'; ListItem.tagName = 'LI'; - class List extends Container { static create(value) { - let tagName = value === 'ordered' ? 'OL' : 'UL'; - let node = super.create(tagName); + const tagName = value === 'ordered' ? 'OL' : 'UL'; + const node = super.create(tagName); if (value === 'checked' || value === 'unchecked') { node.setAttribute('data-checked', value === 'checked'); } @@ -53,26 +54,27 @@ class List extends Container { if (domNode.tagName === 'OL') return 'ordered'; if (domNode.tagName === 'UL') { if (domNode.hasAttribute('data-checked')) { - return domNode.getAttribute('data-checked') === 'true' ? 'checked' : 'unchecked'; - } else { - return 'bullet'; + return domNode.getAttribute('data-checked') === 'true' + ? 'checked' + : 'unchecked'; } + return 'bullet'; } return undefined; } constructor(domNode) { super(domNode); - const listEventHandler = (e) => { + const listEventHandler = e => { if (e.target.parentNode !== domNode) return; - let format = this.statics.formats(domNode); - let blot = Parchment.find(e.target); + const format = this.statics.formats(domNode); + const blot = Parchment.find(e.target); if (format === 'checked') { blot.format('list', 'unchecked'); - } else if(format === 'unchecked') { + } else if (format === 'unchecked') { blot.format('list', 'checked'); } - } + }; domNode.addEventListener('touchstart', listEventHandler); domNode.addEventListener('mousedown', listEventHandler); @@ -93,19 +95,23 @@ class List extends Container { if (blot instanceof ListItem) { super.insertBefore(blot, ref); } else { - let index = ref == null ? this.length() : ref.offset(this); - let after = this.split(index); + const index = ref == null ? this.length() : ref.offset(this); + const after = this.split(index); after.parent.insertBefore(blot, after); } } optimize(context) { super.optimize(context); - let next = this.next; - if (next != null && next.prev === this && - next.statics.blotName === this.statics.blotName && - next.domNode.tagName === this.domNode.tagName && - next.domNode.getAttribute('data-checked') === this.domNode.getAttribute('data-checked')) { + const { next } = this; + if ( + next != null && + next.prev === this && + next.statics.blotName === this.statics.blotName && + next.domNode.tagName === this.domNode.tagName && + next.domNode.getAttribute('data-checked') === + this.domNode.getAttribute('data-checked') + ) { next.moveChildren(this); next.remove(); } @@ -113,18 +119,17 @@ class List extends Container { replace(target) { if (target.statics.blotName !== this.statics.blotName) { - let item = Parchment.create(this.statics.defaultChild); + const item = Parchment.create(this.statics.defaultChild); target.moveChildren(item); this.appendChild(item); } super.replace(target); } } -List.blotName = 'list'; +List.blotName = LIST_BLOT; List.scope = Parchment.Scope.BLOCK_BLOT; List.tagName = ['OL', 'UL']; List.defaultChild = 'list-item'; List.allowedChildren = [ListItem]; - export { ListItem, List as default }; diff --git a/formats/script.js b/formats/script.js index 303e271e1a..4b855a5fbf 100644 --- a/formats/script.js +++ b/formats/script.js @@ -6,9 +6,8 @@ class Script extends Inline { return document.createElement('sup'); } else if (value === 'sub') { return document.createElement('sub'); - } else { - return super.create(value); } + return super.create(value); } static formats(domNode) { diff --git a/formats/size.js b/formats/size.js index c4c0d36ba2..6541e548a5 100644 --- a/formats/size.js +++ b/formats/size.js @@ -1,12 +1,12 @@ import Parchment from 'parchment'; -let SizeClass = new Parchment.Attributor.Class('size', 'ql-size', { +const SizeClass = new Parchment.Attributor.Class('size', 'ql-size', { scope: Parchment.Scope.INLINE, - whitelist: ['small', 'large', 'huge'] + whitelist: ['small', 'large', 'huge'], }); -let SizeStyle = new Parchment.Attributor.Style('size', 'font-size', { +const SizeStyle = new Parchment.Attributor.Style('size', 'font-size', { scope: Parchment.Scope.INLINE, - whitelist: ['10px', '18px', '32px'] + whitelist: ['10px', '18px', '32px'], }); export { SizeClass, SizeStyle }; diff --git a/formats/strike.js b/formats/strike.js index 9376e5b5c9..24764d7f89 100644 --- a/formats/strike.js +++ b/formats/strike.js @@ -1,6 +1,6 @@ import Inline from '../blots/inline'; -class Strike extends Inline { } +class Strike extends Inline {} Strike.blotName = 'strike'; Strike.tagName = 'S'; diff --git a/formats/underline.js b/formats/underline.js index f9d1b6f7a1..500fa77549 100644 --- a/formats/underline.js +++ b/formats/underline.js @@ -1,6 +1,6 @@ import Inline from '../blots/inline'; -class Underline extends Inline { } +class Underline extends Inline {} Underline.blotName = 'underline'; Underline.tagName = 'U'; diff --git a/formats/video.js b/formats/video.js index 7472b29bcf..332e2d5dd1 100644 --- a/formats/video.js +++ b/formats/video.js @@ -1,15 +1,11 @@ import { BlockEmbed } from '../blots/block'; import Link from '../formats/link'; -const ATTRIBUTES = [ - 'height', - 'width' -]; - +const ATTRIBUTES = ['height', 'width']; class Video extends BlockEmbed { static create(value) { - let node = super.create(value); + const node = super.create(value); node.setAttribute('frameborder', '0'); node.setAttribute('allowfullscreen', true); node.setAttribute('src', this.sanitize(value)); @@ -17,7 +13,7 @@ class Video extends BlockEmbed { } static formats(domNode) { - return ATTRIBUTES.reduce(function(formats, attribute) { + return ATTRIBUTES.reduce((formats, attribute) => { if (domNode.hasAttribute(attribute)) { formats[attribute] = domNode.getAttribute(attribute); } @@ -26,7 +22,7 @@ class Video extends BlockEmbed { } static sanitize(url) { - return Link.sanitize(url); + return Link.sanitize(url); // eslint-disable-line import/no-named-as-default-member } static value(domNode) { @@ -49,5 +45,4 @@ Video.blotName = 'video'; Video.className = 'ql-video'; Video.tagName = 'IFRAME'; - export default Video; diff --git a/modules/clipboard.js b/modules/clipboard.js index 97602c1316..26a1e3c798 100644 --- a/modules/clipboard.js +++ b/modules/clipboard.js @@ -13,8 +13,7 @@ import { DirectionAttribute, DirectionStyle } from '../formats/direction'; import { FontStyle } from '../formats/font'; import { SizeStyle } from '../formats/size'; -let debug = logger('quill:clipboard'); - +const debug = logger('quill:clipboard'); const DOM_KEY = '__ql-matcher'; @@ -29,13 +28,13 @@ const CLIPBOARD_CONFIG = [ ['li', matchIndent], ['b', matchAlias.bind(matchAlias, 'bold')], ['i', matchAlias.bind(matchAlias, 'italic')], - ['style', matchIgnore] + ['style', matchIgnore], ]; const ATTRIBUTE_ATTRIBUTORS = [ AlignAttribute, - DirectionAttribute -].reduce(function(memo, attr) { + DirectionAttribute, +].reduce((memo, attr) => { memo[attr.keyName] = attr; return memo; }, {}); @@ -46,13 +45,12 @@ const STYLE_ATTRIBUTORS = [ ColorStyle, DirectionStyle, FontStyle, - SizeStyle -].reduce(function(memo, attr) { + SizeStyle, +].reduce((memo, attr) => { memo[attr.keyName] = attr; return memo; }, {}); - class Clipboard extends Module { constructor(quill, options) { super(quill, options); @@ -61,7 +59,9 @@ class Clipboard extends Module { this.container.setAttribute('contenteditable', true); this.container.setAttribute('tabindex', -1); this.matchers = []; - CLIPBOARD_CONFIG.concat(this.options.matchers).forEach(([selector, matcher]) => { + CLIPBOARD_CONFIG.concat( + this.options.matchers, + ).forEach(([selector, matcher]) => { this.addMatcher(selector, matcher); }); } @@ -72,18 +72,23 @@ class Clipboard extends Module { convert(html) { if (typeof html === 'string') { - this.container.innerHTML = html.replace(/\>\r?\n +\<'); // Remove spaces between tags + this.container.innerHTML = html.replace(/>\r?\n +<'); // Remove spaces between tags return this.convert(); } const formats = this.quill.getFormat(this.quill.selection.savedRange.index); if (formats[CodeBlock.blotName]) { const text = this.container.innerText; - return new Delta().insert(text, { [CodeBlock.blotName]: formats[CodeBlock.blotName] }); + return new Delta().insert(text, { + [CodeBlock.blotName]: formats[CodeBlock.blotName], + }); } - let [elementMatchers, textMatchers] = this.prepareMatching(); + const [elementMatchers, textMatchers] = this.prepareMatching(); let delta = traverse(this.container, elementMatchers, textMatchers); // Remove trailing newline - if (deltaEndsWith(delta, '\n') && delta.ops[delta.ops.length - 1].attributes == null) { + if ( + deltaEndsWith(delta, '\n') && + delta.ops[delta.ops.length - 1].attributes == null + ) { delta = delta.compose(new Delta().retain(delta.length() - 1).delete(1)); } debug.log('convert', this.container.innerHTML, delta); @@ -94,16 +99,18 @@ class Clipboard extends Module { dangerouslyPasteHTML(index, html, source = Quill.sources.API) { if (typeof index === 'string') { return this.quill.setContents(this.convert(index), html); - } else { - let paste = this.convert(html); - return this.quill.updateContents(new Delta().retain(index).concat(paste), source); } + const paste = this.convert(html); + return this.quill.updateContents( + new Delta().retain(index).concat(paste), + source, + ); } onCapturePaste(e) { if (e.defaultPrevented || !this.quill.isEnabled()) return; - let scrollTop = this.quill.scrollingContainer.scrollTop; - let range = this.quill.getSelection(); + const { scrollTop } = this.quill.scrollingContainer; + const range = this.quill.getSelection(); this.container.focus(); this.quill.selection.update(Quill.sources.SILENT); setTimeout(() => { @@ -119,13 +126,17 @@ class Clipboard extends Module { delta = delta.concat(this.convert()).delete(range.length); this.quill.updateContents(delta, Quill.sources.USER); // range.length contributes to delta.length() - this.quill.setSelection(delta.length() - range.length, Quill.sources.SILENT); + this.quill.setSelection( + delta.length() - range.length, + Quill.sources.SILENT, + ); } prepareMatching() { - let elementMatchers = [], textMatchers = []; - this.matchers.forEach((pair) => { - let [selector, matcher] = pair; + const elementMatchers = []; + const textMatchers = []; + this.matchers.forEach(pair => { + const [selector, matcher] = pair; switch (selector) { case Node.TEXT_NODE: textMatchers.push(matcher); @@ -134,7 +145,7 @@ class Clipboard extends Module { elementMatchers.push(matcher); break; default: - [].forEach.call(this.container.querySelectorAll(selector), (node) => { + [].forEach.call(this.container.querySelectorAll(selector), node => { // TODO use weakmap node[DOM_KEY] = node[DOM_KEY] || []; node[DOM_KEY].push(matcher); @@ -147,115 +158,129 @@ class Clipboard extends Module { } Clipboard.DEFAULTS = { matchers: [], - matchVisual: false + matchVisual: false, }; - function applyFormat(delta, format, value) { if (typeof format === 'object') { - return Object.keys(format).reduce(function(delta, key) { - return applyFormat(delta, key, format[key]); + return Object.keys(format).reduce((newDelta, key) => { + return applyFormat(newDelta, key, format[key]); }, delta); - } else { - return delta.reduce(function(delta, op) { - if (op.attributes && op.attributes[format]) { - return delta.push(op); - } else { - return delta.insert(op.insert, extend({}, {[format]: value}, op.attributes)); - } - }, new Delta()); } + return delta.reduce((newDelta, op) => { + if (op.attributes && op.attributes[format]) { + return newDelta.push(op); + } + return newDelta.insert( + op.insert, + extend({}, { [format]: value }, op.attributes), + ); + }, new Delta()); } function computeStyle(node) { if (node.nodeType !== Node.ELEMENT_NODE) return {}; - const DOM_KEY = '__ql-computed-style'; - return node[DOM_KEY] || (node[DOM_KEY] = window.getComputedStyle(node)); + const key = '__ql-computed-style'; + if (node[key] == null) { + node[key] = window.getComputedStyle(node); + } + return node[key]; } function deltaEndsWith(delta, text) { - let endText = ""; - for (let i = delta.ops.length - 1; i >= 0 && endText.length < text.length; --i) { - let op = delta.ops[i]; + let endText = ''; + for ( + let i = delta.ops.length - 1; + i >= 0 && endText.length < text.length; + --i // eslint-disable-line no-plusplus + ) { + const op = delta.ops[i]; if (typeof op.insert !== 'string') break; endText = op.insert + endText; } - return endText.slice(-1*text.length) === text; + return endText.slice(-1 * text.length) === text; } function isLine(node) { - if (node.childNodes.length === 0) return false; // Exclude embed blocks - let style = computeStyle(node); + if (node.childNodes.length === 0) return false; // Exclude embed blocks + const style = computeStyle(node); return ['block', 'list-item'].indexOf(style.display) > -1; } -function traverse(node, elementMatchers, textMatchers) { // Post-order +function traverse(node, elementMatchers, textMatchers) { + // Post-order if (node.nodeType === node.TEXT_NODE) { - return textMatchers.reduce(function(delta, matcher) { + return textMatchers.reduce((delta, matcher) => { return matcher(node, delta); }, new Delta()); } else if (node.nodeType === node.ELEMENT_NODE) { - return [].reduce.call(node.childNodes || [], (delta, childNode) => { - let childrenDelta = traverse(childNode, elementMatchers, textMatchers); - if (childNode.nodeType === node.ELEMENT_NODE) { - childrenDelta = elementMatchers.reduce(function(childrenDelta, matcher) { - return matcher(childNode, childrenDelta); - }, childrenDelta); - childrenDelta = (childNode[DOM_KEY] || []).reduce(function(childrenDelta, matcher) { - return matcher(childNode, childrenDelta); - }, childrenDelta); - } - return delta.concat(childrenDelta); - }, new Delta()); - } else { - return new Delta(); + return [].reduce.call( + node.childNodes || [], + (delta, childNode) => { + let childrenDelta = traverse(childNode, elementMatchers, textMatchers); + if (childNode.nodeType === node.ELEMENT_NODE) { + childrenDelta = elementMatchers.reduce((reducedDelta, matcher) => { + return matcher(childNode, reducedDelta); + }, childrenDelta); + childrenDelta = (childNode[DOM_KEY] || [] + ).reduce((reducedDelta, matcher) => { + return matcher(childNode, reducedDelta); + }, childrenDelta); + } + return delta.concat(childrenDelta); + }, + new Delta(), + ); } + return new Delta(); } - function matchAlias(format, node, delta) { return applyFormat(delta, format, true); } function matchAttributor(node, delta) { - let attributes = Parchment.Attributor.Attribute.keys(node); - let classes = Parchment.Attributor.Class.keys(node); - let styles = Parchment.Attributor.Style.keys(node); - let formats = {}; - attributes.concat(classes).concat(styles).forEach((name) => { - let attr = Parchment.query(name, Parchment.Scope.ATTRIBUTE); - if (attr != null) { - formats[attr.attrName] = attr.value(node); - if (formats[attr.attrName]) return; - } - attr = ATTRIBUTE_ATTRIBUTORS[name]; - if (attr != null && attr.attrName === name) { - formats[attr.attrName] = attr.value(node) || undefined; - } - attr = STYLE_ATTRIBUTORS[name] - if (attr != null && attr.attrName === name) { + const attributes = Parchment.Attributor.Attribute.keys(node); + const classes = Parchment.Attributor.Class.keys(node); + const styles = Parchment.Attributor.Style.keys(node); + const formats = {}; + attributes + .concat(classes) + .concat(styles) + .forEach(name => { + let attr = Parchment.query(name, Parchment.Scope.ATTRIBUTE); + if (attr != null) { + formats[attr.attrName] = attr.value(node); + if (formats[attr.attrName]) return; + } + attr = ATTRIBUTE_ATTRIBUTORS[name]; + if (attr != null && attr.attrName === name) { + formats[attr.attrName] = attr.value(node) || undefined; + } attr = STYLE_ATTRIBUTORS[name]; - formats[attr.attrName] = attr.value(node) || undefined; - } - }); + if (attr != null && attr.attrName === name) { + attr = STYLE_ATTRIBUTORS[name]; + formats[attr.attrName] = attr.value(node) || undefined; + } + }); if (Object.keys(formats).length > 0) { - delta = applyFormat(delta, formats); + return applyFormat(delta, formats); } return delta; } function matchBlot(node, delta) { - let match = Parchment.query(node); + const match = Parchment.query(node); if (match == null) return delta; if (match.prototype instanceof Parchment.Embed) { - let embed = {}; - let value = match.value(node); + const embed = {}; + const value = match.value(node); if (value != null) { embed[match.blotName] = value; - delta = new Delta().insert(embed, match.formats(node)); + return new Delta().insert(embed, match.formats(node)); } } else if (typeof match.formats === 'function') { - delta = applyFormat(delta, match.blotName, match.formats(node)); + return applyFormat(delta, match.blotName, match.formats(node)); } return delta; } @@ -272,11 +297,16 @@ function matchIgnore() { } function matchIndent(node, delta) { - let match = Parchment.query(node); - if (match == null || match.blotName !== 'list-item' || !deltaEndsWith(delta, '\n')) { + const match = Parchment.query(node); + if ( + match == null || + match.blotName !== 'list-item' || + !deltaEndsWith(delta, '\n') + ) { return delta; } - let indent = -1, parent = node.parentNode; + let indent = -1; + let parent = node.parentNode; while (!parent.classList.contains('ql-clipboard')) { if ((Parchment.query(parent) || {}).blotName === 'list') { indent += 1; @@ -284,12 +314,17 @@ function matchIndent(node, delta) { parent = parent.parentNode; } if (indent <= 0) return delta; - return delta.compose(new Delta().retain(delta.length() - 1).retain(1, { indent: indent})); + return delta.compose( + new Delta().retain(delta.length() - 1).retain(1, { indent }), + ); } function matchNewline(node, delta) { if (!deltaEndsWith(delta, '\n')) { - if (isLine(node) || (delta.length() > 0 && node.nextSibling && isLine(node.nextSibling))) { + if ( + isLine(node) || + (delta.length() > 0 && node.nextSibling && isLine(node.nextSibling)) + ) { delta.insert('\n'); } } @@ -297,20 +332,24 @@ function matchNewline(node, delta) { } function matchStyles(node, delta) { - let formats = {}; - let style = node.style || {}; + const formats = {}; + const style = node.style || {}; if (style.fontStyle && computeStyle(node).fontStyle === 'italic') { formats.italic = true; } - if (style.fontWeight && (computeStyle(node).fontWeight.startsWith('bold') || - parseInt(computeStyle(node).fontWeight) >= 700)) { + if ( + style.fontWeight && + (computeStyle(node).fontWeight.startsWith('bold') || + parseInt(computeStyle(node).fontWeight, 10) >= 700) + ) { formats.bold = true; } if (Object.keys(formats).length > 0) { delta = applyFormat(delta, formats); } - if (parseFloat(style.textIndent || 0) > 0) { // Could be 0.5in - delta = new Delta().insert('\t').concat(delta); + if (parseFloat(style.textIndent || 0) > 0) { + // Could be 0.5in + return new Delta().insert('\t').concat(delta); } return delta; } @@ -321,28 +360,39 @@ function matchText(node, delta) { if (node.parentNode.tagName === 'O:P') { return delta.insert(text.trim()); } - if (text.trim().length === 0 && node.parentNode.classList.contains('ql-clipboard')) { + if ( + text.trim().length === 0 && + node.parentNode.classList.contains('ql-clipboard') + ) { return delta; } if (!computeStyle(node.parentNode).whiteSpace.startsWith('pre')) { - // eslint-disable-next-line func-style - let replacer = function(collapse, match) { - match = match.replace(/[^\u00a0]/g, ''); // \u00a0 is nbsp; - return match.length < 1 && collapse ? ' ' : match; + const replacer = (collapse, match) => { + const replaced = match.replace(/[^\u00a0]/g, ''); // \u00a0 is nbsp; + return replaced.length < 1 && collapse ? ' ' : replaced; }; text = text.replace(/\r\n/g, ' ').replace(/\n/g, ' '); - text = text.replace(/\s\s+/g, replacer.bind(replacer, true)); // collapse whitespace - if ((node.previousSibling == null && isLine(node.parentNode)) || - (node.previousSibling != null && isLine(node.previousSibling))) { + text = text.replace(/\s\s+/g, replacer.bind(replacer, true)); // collapse whitespace + if ( + (node.previousSibling == null && isLine(node.parentNode)) || + (node.previousSibling != null && isLine(node.previousSibling)) + ) { text = text.replace(/^\s+/, replacer.bind(replacer, false)); } - if ((node.nextSibling == null && isLine(node.parentNode)) || - (node.nextSibling != null && isLine(node.nextSibling))) { + if ( + (node.nextSibling == null && isLine(node.parentNode)) || + (node.nextSibling != null && isLine(node.nextSibling)) + ) { text = text.replace(/\s+$/, replacer.bind(replacer, false)); } } return delta.insert(text); } - -export { Clipboard as default, matchAttributor, matchBlot, matchNewline, matchText }; +export { + Clipboard as default, + matchAttributor, + matchBlot, + matchNewline, + matchText, +}; diff --git a/modules/history.js b/modules/history.js index 4295924c94..dce49a0bf5 100644 --- a/modules/history.js +++ b/modules/history.js @@ -2,36 +2,47 @@ import Parchment from 'parchment'; import Quill from '../core/quill'; import Module from '../core/module'; - class History extends Module { constructor(quill, options) { super(quill, options); this.lastRecorded = 0; this.ignoreChange = false; this.clear(); - this.quill.on(Quill.events.EDITOR_CHANGE, (eventName, delta, oldDelta, source) => { - if (eventName !== Quill.events.TEXT_CHANGE || this.ignoreChange) return; - if (!this.options.userOnly || source === Quill.sources.USER) { - this.record(delta, oldDelta); - } else { - this.transform(delta); - } - }); - this.quill.keyboard.addBinding({ key: 'z', shortKey: true }, this.undo.bind(this)); - this.quill.keyboard.addBinding({ key: 'z', shortKey: true, shiftKey: true }, this.redo.bind(this)); + this.quill.on( + Quill.events.EDITOR_CHANGE, + (eventName, delta, oldDelta, source) => { + if (eventName !== Quill.events.TEXT_CHANGE || this.ignoreChange) return; + if (!this.options.userOnly || source === Quill.sources.USER) { + this.record(delta, oldDelta); + } else { + this.transform(delta); + } + }, + ); + this.quill.keyboard.addBinding( + { key: 'z', shortKey: true }, + this.undo.bind(this), + ); + this.quill.keyboard.addBinding( + { key: 'z', shortKey: true, shiftKey: true }, + this.redo.bind(this), + ); if (/Win/i.test(navigator.platform)) { - this.quill.keyboard.addBinding({ key: 'y', shortKey: true }, this.redo.bind(this)); + this.quill.keyboard.addBinding( + { key: 'y', shortKey: true }, + this.redo.bind(this), + ); } } change(source, dest) { if (this.stack[source].length === 0) return; - let delta = this.stack[source].pop(); + const delta = this.stack[source].pop(); this.lastRecorded = 0; this.ignoreChange = true; this.quill.updateContents(delta[source], Quill.sources.USER); this.ignoreChange = false; - let index = getLastChangeIndex(delta[source]); + const index = getLastChangeIndex(delta[source]); this.quill.setSelection(index); this.stack[dest].push(delta); } @@ -48,9 +59,12 @@ class History extends Module { if (changeDelta.ops.length === 0) return; this.stack.redo = []; let undoDelta = this.quill.getContents().diff(oldDelta); - let timestamp = Date.now(); - if (this.lastRecorded + this.options.delay > timestamp && this.stack.undo.length > 0) { - let delta = this.stack.undo.pop(); + const timestamp = Date.now(); + if ( + this.lastRecorded + this.options.delay > timestamp && + this.stack.undo.length > 0 + ) { + const delta = this.stack.undo.pop(); undoDelta = undoDelta.compose(delta.undo); changeDelta = delta.redo.compose(changeDelta); } else { @@ -58,7 +72,7 @@ class History extends Module { } this.stack.undo.push({ redo: changeDelta, - undo: undoDelta + undo: undoDelta, }); if (this.stack.undo.length > this.options.maxStack) { this.stack.undo.shift(); @@ -70,11 +84,11 @@ class History extends Module { } transform(delta) { - this.stack.undo.forEach(function(change) { + this.stack.undo.forEach(change => { change.undo = delta.transform(change.undo, true); change.redo = delta.transform(change.redo, true); }); - this.stack.redo.forEach(function(change) { + this.stack.redo.forEach(change => { change.undo = delta.transform(change.undo, true); change.redo = delta.transform(change.redo, true); }); @@ -87,17 +101,17 @@ class History extends Module { History.DEFAULTS = { delay: 1000, maxStack: 100, - userOnly: false + userOnly: false, }; function endsWithNewlineChange(delta) { - let lastOp = delta.ops[delta.ops.length - 1]; + const lastOp = delta.ops[delta.ops.length - 1]; if (lastOp == null) return false; if (lastOp.insert != null) { return typeof lastOp.insert === 'string' && lastOp.insert.endsWith('\n'); } if (lastOp.attributes != null) { - return Object.keys(lastOp.attributes).some(function(attr) { + return Object.keys(lastOp.attributes).some(attr => { return Parchment.query(attr, Parchment.Scope.BLOCK) != null; }); } @@ -105,9 +119,8 @@ function endsWithNewlineChange(delta) { } function getLastChangeIndex(delta) { - let deleteLength = delta.reduce(function(length, op) { - length += (op.delete || 0); - return length; + const deleteLength = delta.reduce((length, op) => { + return length + (op.delete || 0); }, 0); let changeIndex = delta.length() - deleteLength; if (endsWithNewlineChange(delta)) { @@ -116,5 +129,4 @@ function getLastChangeIndex(delta) { return changeIndex; } - export { History as default, getLastChangeIndex }; diff --git a/modules/keyboard.js b/modules/keyboard.js index ae7c88eb42..1c427b61c5 100644 --- a/modules/keyboard.js +++ b/modules/keyboard.js @@ -9,16 +9,17 @@ import Quill from '../core/quill'; import logger from '../core/logger'; import Module from '../core/module'; -let debug = logger('quill:keyboard'); +const debug = logger('quill:keyboard'); const SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey'; - class Keyboard extends Module { static match(evt, binding) { - if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(function(key) { - return (!!binding[key] !== evt[key] && binding[key] !== null); - })) { + if ( + ['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(key => { + return !!binding[key] !== evt[key] && binding[key] !== null; + }) + ) { return false; } return binding.key === evt.key; @@ -27,10 +28,12 @@ class Keyboard extends Module { constructor(quill, options) { super(quill, options); this.bindings = {}; - Object.keys(this.options.bindings).forEach((name) => { - if (name === 'list autofill' && - quill.scroll.whitelist != null && - !quill.scroll.whitelist['list']) { + Object.keys(this.options.bindings).forEach(name => { + if ( + name === 'list autofill' && + quill.scroll.whitelist != null && + !quill.scroll.whitelist.list + ) { return; } if (this.options.bindings[name]) { @@ -38,25 +41,52 @@ class Keyboard extends Module { } }); this.addBinding({ key: 'Enter', shiftKey: null }, handleEnter); - this.addBinding({ key: 'Enter', metaKey: null, ctrlKey: null, altKey: null }, function() {}); + this.addBinding( + { key: 'Enter', metaKey: null, ctrlKey: null, altKey: null }, + () => {}, + ); if (/Firefox/i.test(navigator.userAgent)) { // Need to handle delete and backspace for Firefox in the general case #1171 - this.addBinding({ key: 'Backspace' }, { collapsed: true }, handleBackspace); + this.addBinding( + { key: 'Backspace' }, + { collapsed: true }, + handleBackspace, + ); this.addBinding({ key: 'Delete' }, { collapsed: true }, handleDelete); } else { - this.addBinding({ key: 'Backspace' }, { collapsed: true, prefix: /^.?$/ }, handleBackspace); - this.addBinding({ key: 'Delete' }, { collapsed: true, suffix: /^.?$/ }, handleDelete); + this.addBinding( + { key: 'Backspace' }, + { collapsed: true, prefix: /^.?$/ }, + handleBackspace, + ); + this.addBinding( + { key: 'Delete' }, + { collapsed: true, suffix: /^.?$/ }, + handleDelete, + ); } - this.addBinding({ key: 'Backspace' }, { collapsed: false }, handleDeleteRange); + this.addBinding( + { key: 'Backspace' }, + { collapsed: false }, + handleDeleteRange, + ); this.addBinding({ key: 'Delete' }, { collapsed: false }, handleDeleteRange); - this.addBinding({ key: 'Backspace', altKey: null, ctrlKey: null, metaKey: null, shiftKey: null }, - { collapsed: true, offset: 0 }, - handleBackspace); + this.addBinding( + { + key: 'Backspace', + altKey: null, + ctrlKey: null, + metaKey: null, + shiftKey: null, + }, + { collapsed: true, offset: 0 }, + handleBackspace, + ); this.listen(); } - addBinding(key, context = {}, handler = {}) { - let binding = normalize(key); + addBinding(keyBinding, context = {}, handler = {}) { + const binding = normalize(keyBinding); if (binding == null) { debug.warn('Attempted to add invalid keyboard binding', binding); return; @@ -65,10 +95,10 @@ class Keyboard extends Module { context = { handler: context }; } if (typeof handler === 'function') { - handler = { handler: handler }; + handler = { handler }; } const keys = Array.isArray(binding.key) ? binding.key : [binding.key]; - keys.forEach((key) => { + keys.forEach(key => { const singleBinding = extend({}, binding, { key }, context, handler); this.bindings[singleBinding.key] = this.bindings[singleBinding.key] || []; this.bindings[singleBinding.key].push(singleBinding); @@ -76,51 +106,70 @@ class Keyboard extends Module { } listen() { - this.quill.root.addEventListener('keydown', (evt) => { + this.quill.root.addEventListener('keydown', evt => { if (evt.defaultPrevented) return; - let bindings = (this.bindings[evt.key] || []).filter(function(binding) { - return Keyboard.match(evt, binding); - }); + const bindings = (this.bindings[evt.key] || []).filter(binding => + Keyboard.match(evt, binding), + ); if (bindings.length === 0) return; - let range = this.quill.getSelection(); + const range = this.quill.getSelection(); if (range == null || !this.quill.hasFocus()) return; - let [line, offset] = this.quill.getLine(range.index); - let [leafStart, offsetStart] = this.quill.getLeaf(range.index); - let [leafEnd, offsetEnd] = range.length === 0 ? [leafStart, offsetStart] : this.quill.getLeaf(range.index + range.length); - let prefixText = leafStart instanceof Parchment.Text ? leafStart.value().slice(0, offsetStart) : ''; - let suffixText = leafEnd instanceof Parchment.Text ? leafEnd.value().slice(offsetEnd) : ''; - let curContext = { + const [line, offset] = this.quill.getLine(range.index); + const [leafStart, offsetStart] = this.quill.getLeaf(range.index); + const [leafEnd, offsetEnd] = + range.length === 0 + ? [leafStart, offsetStart] + : this.quill.getLeaf(range.index + range.length); + const prefixText = + leafStart instanceof Parchment.Text + ? leafStart.value().slice(0, offsetStart) + : ''; + const suffixText = + leafEnd instanceof Parchment.Text + ? leafEnd.value().slice(offsetEnd) + : ''; + const curContext = { collapsed: range.length === 0, empty: range.length === 0 && line.length() <= 1, format: this.quill.getFormat(range), - offset: offset, + offset, prefix: prefixText, suffix: suffixText, event: evt, }; - let prevented = bindings.some((binding) => { - if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) return false; - if (binding.empty != null && binding.empty !== curContext.empty) return false; - if (binding.offset != null && binding.offset !== curContext.offset) return false; + const prevented = bindings.some(binding => { + if ( + binding.collapsed != null && + binding.collapsed !== curContext.collapsed + ) + return false; + if (binding.empty != null && binding.empty !== curContext.empty) + return false; + if (binding.offset != null && binding.offset !== curContext.offset) + return false; if (Array.isArray(binding.format)) { // any format is present - if (binding.format.every(function(name) { - return curContext.format[name] == null; - })) { + if (binding.format.every(name => curContext.format[name] == null)) { return false; } } else if (typeof binding.format === 'object') { // all formats must match - if (!Object.keys(binding.format).every(function(name) { - if (binding.format[name] === true) return curContext.format[name] != null; - if (binding.format[name] === false) return curContext.format[name] == null; - return equal(binding.format[name], curContext.format[name]); - })) { + if ( + !Object.keys(binding.format).every(name => { + if (binding.format[name] === true) + return curContext.format[name] != null; + if (binding.format[name] === false) + return curContext.format[name] == null; + return equal(binding.format[name], curContext.format[name]); + }) + ) { return false; } } - if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) return false; - if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) return false; + if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) + return false; + if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) + return false; return binding.handler.call(this, range, curContext) !== true; }); if (prevented) { @@ -132,27 +181,29 @@ class Keyboard extends Module { Keyboard.DEFAULTS = { bindings: { - 'bold' : makeFormatHandler('bold'), - 'italic' : makeFormatHandler('italic'), - 'underline' : makeFormatHandler('underline'), - 'indent': { + bold: makeFormatHandler('bold'), + italic: makeFormatHandler('italic'), + underline: makeFormatHandler('underline'), + indent: { // highlight tab or tab at beginning of list, indent or blockquote key: 'Tab', format: ['blockquote', 'indent', 'list'], - handler: function(range, context) { + handler(range, context) { if (context.collapsed && context.offset !== 0) return true; this.quill.format('indent', '+1', Quill.sources.USER); - } + return false; + }, }, - 'outdent': { + outdent: { key: 'Tab', shiftKey: true, format: ['blockquote', 'indent', 'list'], // highlight tab or tab at beginning of list, indent or blockquote - handler: function(range, context) { + handler(range, context) { if (context.collapsed && context.offset !== 0) return true; this.quill.format('indent', '-1', Quill.sources.USER); - } + return false; + }, }, 'outdent backspace': { key: 'Backspace', @@ -163,13 +214,13 @@ Keyboard.DEFAULTS = { altKey: null, format: ['indent', 'list'], offset: 0, - handler: function(range, context) { + handler(range, context) { if (context.format.indent != null) { this.quill.format('indent', '-1', Quill.sources.USER); } else if (context.format.list != null) { this.quill.format('list', false, Quill.sources.USER); } - } + }, }, 'indent code-block': makeCodeBlockHandler(true), 'outdent code-block': makeCodeBlockHandler(false), @@ -178,77 +229,81 @@ Keyboard.DEFAULTS = { shiftKey: true, collapsed: true, prefix: /\t$/, - handler: function(range) { + handler(range) { this.quill.deleteText(range.index - 1, 1, Quill.sources.USER); - } + }, }, - 'tab': { + tab: { key: 'Tab', - handler: function(range) { + handler(range) { this.quill.history.cutoff(); - let delta = new Delta().retain(range.index) - .delete(range.length) - .insert('\t'); + const delta = new Delta() + .retain(range.index) + .delete(range.length) + .insert('\t'); this.quill.updateContents(delta, Quill.sources.USER); this.quill.history.cutoff(); this.quill.setSelection(range.index + 1, Quill.sources.SILENT); - } + }, }, 'list empty enter': { key: 'Enter', collapsed: true, format: ['list'], empty: true, - handler: function(range, context) { + handler(range, context) { this.quill.format('list', false, Quill.sources.USER); if (context.format.indent) { this.quill.format('indent', false, Quill.sources.USER); } - } + }, }, 'checklist enter': { key: 'Enter', collapsed: true, format: { list: 'checked' }, - handler: function(range) { - let [line, offset] = this.quill.getLine(range.index); - let delta = new Delta().retain(range.index) - .insert('\n', { list: 'checked' }) - .retain(line.length() - offset - 1) - .retain(1, { list: 'unchecked' }); + handler(range) { + const [line, offset] = this.quill.getLine(range.index); + const delta = new Delta() + .retain(range.index) + .insert('\n', { list: 'checked' }) + .retain(line.length() - offset - 1) + .retain(1, { list: 'unchecked' }); this.quill.updateContents(delta, Quill.sources.USER); this.quill.setSelection(range.index + 1, Quill.sources.SILENT); this.quill.scrollIntoView(); - } + }, }, 'header enter': { key: 'Enter', collapsed: true, format: ['header'], suffix: /^$/, - handler: function(range, context) { - let [line, offset] = this.quill.getLine(range.index); - let delta = new Delta().retain(range.index) - .insert('\n', context.format) - .retain(line.length() - offset - 1) - .retain(1, { header: null }); + handler(range, context) { + const [line, offset] = this.quill.getLine(range.index); + const delta = new Delta() + .retain(range.index) + .insert('\n', context.format) + .retain(line.length() - offset - 1) + .retain(1, { header: null }); this.quill.updateContents(delta, Quill.sources.USER); this.quill.setSelection(range.index + 1, Quill.sources.SILENT); this.quill.scrollIntoView(); - } + }, }, 'list autofill': { key: ' ', collapsed: true, format: { list: false }, - prefix: /^\s*?(\d+\.|-|\[ ?\]|\[x\])$/, - handler: function(range, context) { - let length = context.prefix.length; - let [line, offset] = this.quill.getLine(range.index); + prefix: /^\s*?(1\.|-|\[ ?\]|\[x\])$/, + handler(range, context) { + const { length } = context.prefix; + const [line, offset] = this.quill.getLine(range.index); if (offset > length) return true; let value; switch (context.prefix.trim()) { - case '[]': case '[ ]': + case '[]': + case '[ ]': value = 'unchecked'; break; case '[x]': @@ -262,14 +317,16 @@ Keyboard.DEFAULTS = { } this.quill.insertText(range.index, ' ', Quill.sources.USER); this.quill.history.cutoff(); - let delta = new Delta().retain(range.index - offset) - .delete(length + 1) - .retain(line.length() - 2 - offset) - .retain(1, { list: value }); + const delta = new Delta() + .retain(range.index - offset) + .delete(length + 1) + .retain(line.length() - 2 - offset) + .retain(1, { list: value }); this.quill.updateContents(delta, Quill.sources.USER); this.quill.history.cutoff(); this.quill.setSelection(range.index - length, Quill.sources.SILENT); - } + return false; + }, }, 'code exit': { key: 'Enter', @@ -277,7 +334,7 @@ Keyboard.DEFAULTS = { format: ['code-block'], prefix: /\n\n$/, suffix: /^\s+$/, - handler: function(range) { + handler(range) { const [line, offset] = this.quill.getLine(range.index); const delta = new Delta() .retain(range.index + line.length() - offset - 2) @@ -285,13 +342,13 @@ Keyboard.DEFAULTS = { .delete(1); this.quill.updateContents(delta, Quill.sources.USER); this.quill.setSelection(range.index - 1, Quill.sources.SILENT); - } + }, }, 'embed left': makeEmbedArrowHandler('ArrowLeft', false), 'embed left shift': makeEmbedArrowHandler('ArrowLeft', true), 'embed right': makeEmbedArrowHandler('ArrowRight', false), - 'embed right shift': makeEmbedArrowHandler('ArrowRight', true) - } + 'embed right shift': makeEmbedArrowHandler('ArrowRight', true), + }, }; function makeEmbedArrowHandler(key, shiftKey) { @@ -300,80 +357,100 @@ function makeEmbedArrowHandler(key, shiftKey) { key, shiftKey, [where]: /^$/, - handler: function(range) { - let index = range.index; + handler(range) { + let { index } = range; if (key === 'ArrowRight') { - index += (range.length + 1); + index += range.length + 1; } - const [leaf, ] = this.quill.getLeaf(index); + const [leaf] = this.quill.getLeaf(index); if (!(leaf instanceof Embed)) return true; if (key === 'ArrowLeft') { if (shiftKey) { - this.quill.setSelection(range.index - 1, range.length + 1, Quill.sources.USER); + this.quill.setSelection( + range.index - 1, + range.length + 1, + Quill.sources.USER, + ); } else { this.quill.setSelection(range.index - 1, Quill.sources.USER); } + } + if (shiftKey) { + this.quill.setSelection( + range.index, + range.length + 1, + Quill.sources.USER, + ); } else { - if (shiftKey) { - this.quill.setSelection(range.index, range.length + 1, Quill.sources.USER); - } else { - this.quill.setSelection(range.index + range.length + 1, Quill.sources.USER); - } + this.quill.setSelection( + range.index + range.length + 1, + Quill.sources.USER, + ); } return false; - } + }, }; } - function handleBackspace(range, context) { if (range.index === 0 || this.quill.getLength() <= 1) return; - let [line, ] = this.quill.getLine(range.index); + const [line] = this.quill.getLine(range.index); let formats = {}; if (context.offset === 0) { - let [prev, ] = this.quill.getLine(range.index - 1); + const [prev] = this.quill.getLine(range.index - 1); if (prev != null && prev.length() > 1) { - let curFormats = line.formats(); - let prevFormats = this.quill.getFormat(range.index-1, 1); + const curFormats = line.formats(); + const prevFormats = this.quill.getFormat(range.index - 1, 1); formats = DeltaOp.attributes.diff(curFormats, prevFormats) || {}; } } // Check for astral symbols - let length = /[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(context.prefix) ? 2 : 1; - this.quill.deleteText(range.index-length, length, Quill.sources.USER); + const length = /[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(context.prefix) ? 2 : 1; + this.quill.deleteText(range.index - length, length, Quill.sources.USER); if (Object.keys(formats).length > 0) { - this.quill.formatLine(range.index-length, length, formats, Quill.sources.USER); + this.quill.formatLine( + range.index - length, + length, + formats, + Quill.sources.USER, + ); } this.quill.focus(); } function handleDelete(range, context) { // Check for astral symbols - let length = /^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(context.suffix) ? 2 : 1; + const length = /^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(context.suffix) ? 2 : 1; if (range.index >= this.quill.getLength() - length) return; - let formats = {}, nextLength = 0; - let [line, ] = this.quill.getLine(range.index); + let formats = {}; + let nextLength = 0; + const [line] = this.quill.getLine(range.index); if (context.offset >= line.length() - 1) { - let [next, ] = this.quill.getLine(range.index + 1); + const [next] = this.quill.getLine(range.index + 1); if (next) { - let curFormats = line.formats(); - let nextFormats = this.quill.getFormat(range.index, 1); + const curFormats = line.formats(); + const nextFormats = this.quill.getFormat(range.index, 1); formats = DeltaOp.attributes.diff(curFormats, nextFormats) || {}; nextLength = next.length(); } } this.quill.deleteText(range.index, length, Quill.sources.USER); if (Object.keys(formats).length > 0) { - this.quill.formatLine(range.index + nextLength - 1, length, formats, Quill.sources.USER); + this.quill.formatLine( + range.index + nextLength - 1, + length, + formats, + Quill.sources.USER, + ); } } function handleDeleteRange(range) { - let lines = this.quill.getLines(range); + const lines = this.quill.getLines(range); let formats = {}; if (lines.length > 1) { - let firstFormats = lines[0].formats(); - let lastFormats = lines[lines.length - 1].formats(); + const firstFormats = lines[0].formats(); + const lastFormats = lines[lines.length - 1].formats(); formats = DeltaOp.attributes.diff(lastFormats, firstFormats) || {}; } this.quill.deleteText(range, Quill.sources.USER); @@ -386,20 +463,23 @@ function handleDeleteRange(range) { function handleEnter(range, context) { if (range.length > 0) { - this.quill.scroll.deleteAt(range.index, range.length); // So we do not trigger text-change + this.quill.scroll.deleteAt(range.index, range.length); // So we do not trigger text-change } - let lineFormats = Object.keys(context.format).reduce(function(lineFormats, format) { - if (Parchment.query(format, Parchment.Scope.BLOCK) && !Array.isArray(context.format[format])) { - lineFormats[format] = context.format[format]; + const lineFormats = Object.keys(context.format).reduce((formats, format) => { + if ( + Parchment.query(format, Parchment.Scope.BLOCK) && + !Array.isArray(context.format[format]) + ) { + formats[format] = context.format[format]; } - return lineFormats; + return formats; }, {}); this.quill.insertText(range.index, '\n', lineFormats, Quill.sources.USER); // Earlier scroll.deleteAt might have messed up our selection, // so insertText's built in selection preservation is not reliable this.quill.setSelection(range.index + 1, Quill.sources.SILENT); this.quill.focus(); - Object.keys(context.format).forEach((name) => { + Object.keys(context.format).forEach(name => { if (lineFormats[name] != null) return; if (Array.isArray(context.format[name])) return; if (name === 'link') return; @@ -411,17 +491,20 @@ function makeCodeBlockHandler(indent) { return { key: 'Tab', shiftKey: !indent, - format: {'code-block': true }, - handler: function(range) { - let CodeBlock = Parchment.query('code-block'); - let index = range.index, length = range.length; - let [block, offset] = this.quill.scroll.descendant(CodeBlock, index); + format: { 'code-block': true }, + handler(range) { + const CodeBlock = Parchment.query('code-block'); + let { index, length } = range; + const [block, blockOffset] = this.quill.scroll.descendant( + CodeBlock, + index, + ); if (block == null) return; - let scrollIndex = this.quill.getIndex(block); - let start = block.newlineIndex(offset, true) + 1; - let end = block.newlineIndex(scrollIndex + offset + length); - let lines = block.domNode.textContent.slice(start, end).split('\n'); - offset = 0; + const scrollIndex = this.quill.getIndex(block); + const start = block.newlineIndex(blockOffset, true) + 1; + const end = block.newlineIndex(scrollIndex + blockOffset + length); + const lines = block.domNode.textContent.slice(start, end).split('\n'); + let offset = 0; lines.forEach((line, i) => { if (indent) { block.insertAt(start + offset, CodeBlock.TAB); @@ -444,7 +527,7 @@ function makeCodeBlockHandler(indent) { }); this.quill.update(Quill.sources.USER); this.quill.setSelection(index, length, Quill.sources.SILENT); - } + }, }; } @@ -452,9 +535,9 @@ function makeFormatHandler(format) { return { key: format[0], shortKey: true, - handler: function(range, context) { + handler(range, context) { this.quill.format(format, !context.format[format], Quill.sources.USER); - } + }, }; } @@ -473,5 +556,4 @@ function normalize(binding) { return binding; } - export { Keyboard as default, SHORTKEY, normalize }; diff --git a/modules/syntax.js b/modules/syntax.js index 15049ad05f..6496e1ddf1 100644 --- a/modules/syntax.js +++ b/modules/syntax.js @@ -3,6 +3,7 @@ import Quill from '../core/quill'; import Module from '../core/module'; import CodeBlock from '../formats/code'; +const CODE_TOKEN_CLASS = 'ql-token'; class SyntaxCodeBlock extends CodeBlock { static formats(node) { @@ -16,15 +17,15 @@ class SyntaxCodeBlock extends CodeBlock { } highlight(highlight) { - let text = this.domNode.textContent; + const text = this.domNode.textContent; if (this.cachedText !== text) { if (text.trim().length > 0 || this.cachedText == null) { const language = this.statics.formats(this.domNode); const subset = typeof language === 'string' ? [language] : undefined; this.domNode.innerHTML = highlight(text, subset); this.domNode.normalize(); - [].slice.call(this.domNode.querySelectorAll('span')).forEach((token) => { - token.classList.add(CodeToken.className); + [].slice.call(this.domNode.querySelectorAll('span')).forEach(token => { + token.classList.add(CODE_TOKEN_CLASS); }); this.attach(); } @@ -34,7 +35,6 @@ class SyntaxCodeBlock extends CodeBlock { } SyntaxCodeBlock.className = 'ql-syntax'; - class CodeToken extends Inline { static formats(node) { while (node != null) { @@ -48,8 +48,7 @@ class CodeToken extends Inline { } } CodeToken.blotName = 'code-token'; -CodeToken.className = 'ql-token'; - +CodeToken.className = CODE_TOKEN_CLASS; class Syntax extends Module { static register() { @@ -60,7 +59,9 @@ class Syntax extends Module { constructor(quill, options) { super(quill, options); if (typeof this.options.highlight !== 'function') { - throw new Error('Syntax module requires highlight.js. Please include the library on the page before Quill.'); + throw new Error( + 'Syntax module requires highlight.js. Please include the library on the page before Quill.', + ); } let timer = null; this.quill.on(Quill.events.SCROLL_OPTIMIZE, () => { @@ -76,8 +77,8 @@ class Syntax extends Module { highlight() { if (this.quill.selection.composing) return; this.quill.update(Quill.sources.USER); - let range = this.quill.getSelection(); - this.quill.scroll.descendants(SyntaxCodeBlock).forEach((code) => { + const range = this.quill.getSelection(); + this.quill.scroll.descendants(SyntaxCodeBlock).forEach(code => { code.highlight(this.options.highlight); }); this.quill.update(Quill.sources.SILENT); @@ -87,15 +88,14 @@ class Syntax extends Module { } } Syntax.DEFAULTS = { - highlight: (function() { + highlight: (() => { if (window.hljs == null) return null; - return function(text, subset) { - let result = window.hljs.highlightAuto(text, subset); + return (text, subset) => { + const result = window.hljs.highlightAuto(text, subset); return result.value; }; })(), - interval: 1000 + interval: 1000, }; - -export { SyntaxCodeBlock as CodeBlock, CodeToken, Syntax as default}; +export { SyntaxCodeBlock as CodeBlock, CodeToken, Syntax as default }; diff --git a/modules/toolbar.js b/modules/toolbar.js index 8cbae0809f..517b9272fd 100644 --- a/modules/toolbar.js +++ b/modules/toolbar.js @@ -4,14 +4,13 @@ import Quill from '../core/quill'; import logger from '../core/logger'; import Module from '../core/module'; -let debug = logger('quill:toolbar'); - +const debug = logger('quill:toolbar'); class Toolbar extends Module { constructor(quill, options) { super(quill, options); if (Array.isArray(this.options.container)) { - let container = document.createElement('div'); + const container = document.createElement('div'); addControls(container, this.options.container); quill.container.parentNode.insertBefore(container, quill.container); this.container = container; @@ -26,19 +25,22 @@ class Toolbar extends Module { this.container.classList.add('ql-toolbar'); this.controls = []; this.handlers = {}; - Object.keys(this.options.handlers).forEach((format) => { + Object.keys(this.options.handlers).forEach(format => { this.addHandler(format, this.options.handlers[format]); }); - [].forEach.call(this.container.querySelectorAll('button, select'), (input) => { - this.attach(input); - }); + [].forEach.call( + this.container.querySelectorAll('button, select'), + input => { + this.attach(input); + }, + ); this.quill.on(Quill.events.EDITOR_CHANGE, (type, range) => { if (type === Quill.events.SELECTION_CHANGE) { this.update(range); } }); this.quill.on(Quill.events.SCROLL_OPTIMIZE, () => { - let [range, ] = this.quill.selection.getRange(); // quill.getSelection triggers update + const [range] = this.quill.selection.getRange(); // quill.getSelection triggers update this.update(range); }); } @@ -48,7 +50,7 @@ class Toolbar extends Module { } attach(input) { - let format = [].find.call(input.classList, (className) => { + let format = [].find.call(input.classList, className => { return className.indexOf('ql-') === 0; }); if (!format) return; @@ -57,7 +59,10 @@ class Toolbar extends Module { input.setAttribute('type', 'button'); } if (this.handlers[format] == null) { - if (this.quill.scroll.whitelist != null && this.quill.scroll.whitelist[format] == null) { + if ( + this.quill.scroll.whitelist != null && + this.quill.scroll.whitelist[format] == null + ) { debug.warn('ignoring attaching to disabled format', format, input); return; } @@ -66,12 +71,12 @@ class Toolbar extends Module { return; } } - let eventName = input.tagName === 'SELECT' ? 'change' : 'click'; - input.addEventListener(eventName, (e) => { + const eventName = input.tagName === 'SELECT' ? 'change' : 'click'; + input.addEventListener(eventName, e => { let value; if (input.tagName === 'SELECT') { if (input.selectedIndex < 0) return; - let selected = input.options[input.selectedIndex]; + const selected = input.options[input.selectedIndex]; if (selected.hasAttribute('selected')) { value = false; } else { @@ -86,17 +91,19 @@ class Toolbar extends Module { e.preventDefault(); } this.quill.focus(); - let [range, ] = this.quill.selection.getRange(); + const [range] = this.quill.selection.getRange(); if (this.handlers[format] != null) { this.handlers[format].call(this, value); } else if (Parchment.query(format).prototype instanceof Parchment.Embed) { - value = prompt(`Enter ${format}`); + value = prompt(`Enter ${format}`); // eslint-disable-line no-alert if (!value) return; - this.quill.updateContents(new Delta() - .retain(range.index) - .delete(range.length) - .insert({ [format]: value }) - , Quill.sources.USER); + this.quill.updateContents( + new Delta() + .retain(range.index) + .delete(range.length) + .insert({ [format]: value }), + Quill.sources.USER, + ); } else { this.quill.format(format, value, Quill.sources.USER); } @@ -107,9 +114,9 @@ class Toolbar extends Module { } update(range) { - let formats = range == null ? {} : this.quill.getFormat(range); - this.controls.forEach(function(pair) { - let [format, input] = pair; + const formats = range == null ? {} : this.quill.getFormat(range); + this.controls.forEach(pair => { + const [format, input] = pair; if (input.tagName === 'SELECT') { let option; if (range == null) { @@ -119,40 +126,39 @@ class Toolbar extends Module { } else if (!Array.isArray(formats[format])) { let value = formats[format]; if (typeof value === 'string') { - value = value.replace(/\"/g, '\\"'); + value = value.replace(/"/g, '\\"'); } option = input.querySelector(`option[value="${value}"]`); } if (option == null) { - input.value = ''; // TODO make configurable? + input.value = ''; // TODO make configurable? input.selectedIndex = -1; } else { option.selected = true; } + } else if (range == null) { + input.classList.remove('ql-active'); + } else if (input.hasAttribute('value')) { + // both being null should match (default values) + // '1' should match with 1 (headers) + const isActive = + formats[format] === input.getAttribute('value') || + (formats[format] != null && + formats[format].toString() === input.getAttribute('value')) || + (formats[format] == null && !input.getAttribute('value')); + input.classList.toggle('ql-active', isActive); } else { - if (range == null) { - input.classList.remove('ql-active'); - } else if (input.hasAttribute('value')) { - // both being null should match (default values) - // '1' should match with 1 (headers) - let isActive = formats[format] === input.getAttribute('value') || - (formats[format] != null && formats[format].toString() === input.getAttribute('value')) || - (formats[format] == null && !input.getAttribute('value')); - input.classList.toggle('ql-active', isActive); - } else { - input.classList.toggle('ql-active', formats[format] != null); - } + input.classList.toggle('ql-active', formats[format] != null); } }); } } Toolbar.DEFAULTS = {}; - function addButton(container, format, value) { - let input = document.createElement('button'); + const input = document.createElement('button'); input.setAttribute('type', 'button'); - input.classList.add('ql-' + format); + input.classList.add(`ql-${format}`); if (value != null) { input.value = value; } @@ -163,15 +169,15 @@ function addControls(container, groups) { if (!Array.isArray(groups[0])) { groups = [groups]; } - groups.forEach(function(controls) { - let group = document.createElement('span'); + groups.forEach(controls => { + const group = document.createElement('span'); group.classList.add('ql-formats'); - controls.forEach(function(control) { + controls.forEach(control => { if (typeof control === 'string') { addButton(group, control); } else { - let format = Object.keys(control)[0]; - let value = control[format]; + const format = Object.keys(control)[0]; + const value = control[format]; if (Array.isArray(value)) { addSelect(group, format, value); } else { @@ -184,10 +190,10 @@ function addControls(container, groups) { } function addSelect(container, format, values) { - let input = document.createElement('select'); - input.classList.add('ql-' + format); - values.forEach(function(value) { - let option = document.createElement('option'); + const input = document.createElement('select'); + input.classList.add(`ql-${format}`); + values.forEach(value => { + const option = document.createElement('option'); if (value !== false) { option.setAttribute('value', value); } else { @@ -201,12 +207,12 @@ function addSelect(container, format, values) { Toolbar.DEFAULTS = { container: null, handlers: { - clean: function() { - let range = this.quill.getSelection(); + clean: () => { + const range = this.quill.getSelection(); if (range == null) return; - if (range.length == 0) { - let formats = this.quill.getFormat(); - Object.keys(formats).forEach((name) => { + if (range.length === 0) { + const formats = this.quill.getFormat(); + Object.keys(formats).forEach(name => { // Clean functionality in existing apps only clean inline formats if (Parchment.query(name, Parchment.Scope.INLINE) != null) { this.quill.format(name, false); @@ -216,8 +222,8 @@ Toolbar.DEFAULTS = { this.quill.removeFormat(range, Quill.sources.USER); } }, - direction: function(value) { - let align = this.quill.getFormat()['align']; + direction: value => { + const { align } = this.quill.getFormat(); if (value === 'rtl' && align == null) { this.quill.format('align', 'right', Quill.sources.USER); } else if (!value && align === 'right') { @@ -225,27 +231,27 @@ Toolbar.DEFAULTS = { } this.quill.format('direction', value, Quill.sources.USER); }, - indent: function(value) { - let range = this.quill.getSelection(); - let formats = this.quill.getFormat(range); - let indent = parseInt(formats.indent || 0); + indent: value => { + const range = this.quill.getSelection(); + const formats = this.quill.getFormat(range); + const indent = parseInt(formats.indent || 0, 10); if (value === '+1' || value === '-1') { - let modifier = (value === '+1') ? 1 : -1; + let modifier = value === '+1' ? 1 : -1; if (formats.direction === 'rtl') modifier *= -1; this.quill.format('indent', indent + modifier, Quill.sources.USER); } }, - link: function(value) { + link: value => { if (value === true) { - value = prompt('Enter link URL:'); + value = prompt('Enter link URL:'); // eslint-disable-line no-alert } this.quill.format('link', value, Quill.sources.USER); }, - list: function(value) { - let range = this.quill.getSelection(); - let formats = this.quill.getFormat(range); + list: value => { + const range = this.quill.getSelection(); + const formats = this.quill.getFormat(range); if (value === 'check') { - if (formats['list'] === 'checked' || formats['list'] === 'unchecked') { + if (formats.list === 'checked' || formats.list === 'unchecked') { this.quill.format('list', false, Quill.sources.USER); } else { this.quill.format('list', 'unchecked', Quill.sources.USER); @@ -253,9 +259,8 @@ Toolbar.DEFAULTS = { } else { this.quill.format('list', value, Quill.sources.USER); } - } - } -} - + }, + }, +}; export { Toolbar as default, addControls }; diff --git a/package.json b/package.json index 26b05e52b1..a57838be5d 100644 --- a/package.json +++ b/package.json @@ -84,21 +84,6 @@ "bugs": { "url": "https://github.com/quilljs/quill/issues" }, - "scripts": { - "build": "npm run lint && webpack --config _develop/webpack.config.js; rm dist/quill.core dist/quill.bubble dist/quill.snow;", - "build:release": "./_develop/scripts/release.sh", - "lint": "eslint blots", - "lint:old": "eslint blots core formats modules themes ui", - "start": "npm run build; bundle exec foreman start -f _develop/procfile", - "test": "npm run test:unit", - "test:all": "npm run test:unit; npm run test:functional", - "test:functional": "./_develop/scripts/webdriver.sh", - "test:unit": "npm run build; karma start _develop/karma.config.js", - "test:coverage": "webpack --env.coverage --config _develop/webpack.config.js; karma start _develop/karma.config.js --reporters coverage", - "travis": "karma start _develop/karma.config.js --reporters dots,saucelabs", - "webdriver:start": "webdriver-manager start", - "webdriver:update": "webdriver-manager update" - }, "eslintConfig": { "extends": [ "airbnb", @@ -107,8 +92,7 @@ "env": { "browser": true, "commonjs": true, - "es6": true, - "jasmine": true + "es6": true }, "plugins": [ "prettier" @@ -118,6 +102,16 @@ "off" ], "class-methods-use-this": ["off"], + "import/no-extraneous-dependencies": [ + "error", + { + "devDependencies": [ + "_develop/*.js", + "test/**/*.js" + ] + } + ], + "no-param-reassign": ["off"], "no-use-before-define": [ "error", { @@ -126,15 +120,6 @@ ], "import/named": [ "error" - ], - "import/no-extraneous-dependencies": [ - "error", - { - "devDependencies": [ - "**/*.test.js", - "**/*.stories.js" - ] - } ] } }, @@ -142,6 +127,20 @@ "singleQuote": true, "trailingComma": "all" }, + "scripts": { + "build": "npm run lint && webpack --config _develop/webpack.config.js; rm dist/quill.core dist/quill.bubble dist/quill.snow;", + "build:release": "./_develop/scripts/release.sh", + "lint": "eslint blots core formats modules themes ui test", + "start": "npm run build; bundle exec foreman start -f _develop/procfile", + "test": "npm run test:unit", + "test:all": "npm run test:unit; npm run test:functional", + "test:functional": "./_develop/scripts/webdriver.sh", + "test:unit": "npm run build; karma start _develop/karma.config.js", + "test:coverage": "webpack --env.coverage --config _develop/webpack.config.js; karma start _develop/karma.config.js --reporters coverage", + "travis": "karma start _develop/karma.config.js --reporters dots,saucelabs", + "webdriver:start": "webdriver-manager start", + "webdriver:update": "webdriver-manager update" + }, "keywords": [ "editor", "rich text", diff --git a/quill.js b/quill.js index da469ad615..2747900e57 100644 --- a/quill.js +++ b/quill.js @@ -1,8 +1,12 @@ import Quill from './core'; import { AlignClass, AlignStyle } from './formats/align'; -import { DirectionAttribute, DirectionClass, DirectionStyle } from './formats/direction'; -import { IndentClass as Indent } from './formats/indent'; +import { + DirectionAttribute, + DirectionClass, + DirectionStyle, +} from './formats/direction'; +import Indent from './formats/indent'; import Blockquote from './formats/blockquote'; import Header from './formats/header'; @@ -38,67 +42,70 @@ import Tooltip from './ui/tooltip'; import BubbleTheme from './themes/bubble'; import SnowTheme from './themes/snow'; - -Quill.register({ - 'attributors/attribute/direction': DirectionAttribute, - - 'attributors/class/align': AlignClass, - 'attributors/class/background': BackgroundClass, - 'attributors/class/color': ColorClass, - 'attributors/class/direction': DirectionClass, - 'attributors/class/font': FontClass, - 'attributors/class/size': SizeClass, - - 'attributors/style/align': AlignStyle, - 'attributors/style/background': BackgroundStyle, - 'attributors/style/color': ColorStyle, - 'attributors/style/direction': DirectionStyle, - 'attributors/style/font': FontStyle, - 'attributors/style/size': SizeStyle -}, true); - - -Quill.register({ - 'formats/align': AlignClass, - 'formats/direction': DirectionClass, - 'formats/indent': Indent, - - 'formats/background': BackgroundStyle, - 'formats/color': ColorStyle, - 'formats/font': FontClass, - 'formats/size': SizeClass, - - 'formats/blockquote': Blockquote, - 'formats/code-block': CodeBlock, - 'formats/header': Header, - 'formats/list': List, - - 'formats/bold': Bold, - 'formats/code': InlineCode, - 'formats/italic': Italic, - 'formats/link': Link, - 'formats/script': Script, - 'formats/strike': Strike, - 'formats/underline': Underline, - - 'formats/formula': Formula, - 'formats/image': Image, - 'formats/video': Video, - - 'formats/list/item': ListItem, - - 'modules/syntax': Syntax, - 'modules/toolbar': Toolbar, - - 'themes/bubble': BubbleTheme, - 'themes/snow': SnowTheme, - - 'ui/icons': Icons, - 'ui/picker': Picker, - 'ui/icon-picker': IconPicker, - 'ui/color-picker': ColorPicker, - 'ui/tooltip': Tooltip -}, true); - +Quill.register( + { + 'attributors/attribute/direction': DirectionAttribute, + + 'attributors/class/align': AlignClass, + 'attributors/class/background': BackgroundClass, + 'attributors/class/color': ColorClass, + 'attributors/class/direction': DirectionClass, + 'attributors/class/font': FontClass, + 'attributors/class/size': SizeClass, + + 'attributors/style/align': AlignStyle, + 'attributors/style/background': BackgroundStyle, + 'attributors/style/color': ColorStyle, + 'attributors/style/direction': DirectionStyle, + 'attributors/style/font': FontStyle, + 'attributors/style/size': SizeStyle, + }, + true, +); + +Quill.register( + { + 'formats/align': AlignClass, + 'formats/direction': DirectionClass, + 'formats/indent': Indent, + + 'formats/background': BackgroundStyle, + 'formats/color': ColorStyle, + 'formats/font': FontClass, + 'formats/size': SizeClass, + + 'formats/blockquote': Blockquote, + 'formats/code-block': CodeBlock, + 'formats/header': Header, + 'formats/list': List, + + 'formats/bold': Bold, + 'formats/code': InlineCode, + 'formats/italic': Italic, + 'formats/link': Link, + 'formats/script': Script, + 'formats/strike': Strike, + 'formats/underline': Underline, + + 'formats/formula': Formula, + 'formats/image': Image, + 'formats/video': Video, + + 'formats/list/item': ListItem, + + 'modules/syntax': Syntax, + 'modules/toolbar': Toolbar, + + 'themes/bubble': BubbleTheme, + 'themes/snow': SnowTheme, + + 'ui/icons': Icons, + 'ui/picker': Picker, + 'ui/icon-picker': IconPicker, + 'ui/color-picker': ColorPicker, + 'ui/tooltip': Tooltip, + }, + true, +); export default Quill; diff --git a/test/functional/epic.js b/test/functional/epic.js index faecbd7f14..94ea0dd99d 100644 --- a/test/functional/epic.js +++ b/test/functional/epic.js @@ -1,3 +1,5 @@ +/* global browser */ + const KEYS = { Null: '\uE000', Backspace: '\uE003', @@ -9,13 +11,14 @@ const KEYS = { Right: '\uE014', Down: '\uE015', Delete: '\uE017', - Short: (process.platform === 'darwin' ? '\uE03D' : '\uE009') + Short: process.platform === 'darwin' ? '\uE03D' : '\uE009', }; -var CHAPTER = 'Chapter 1. Loomings.'; -var P1 = 'Call me Ishmael. Some years ago—never mind how long precisely-having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people’s hats off—then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me.' -var P2 = 'There now is your insular city of the Manhattoes, belted round by wharves as Indian isles by coral reefs—commerce surrounds it with her surf. Right and left, the streets take you waterward. Its extreme downtown is the battery, where that noble mole is washed by waves, and cooled by breezes, which a few hours previous were out of sight of land. Look at the crowds of water-gazers there.' - +const CHAPTER = 'Chapter 1. Loomings.'; +const P1 = + 'Call me Ishmael. Some years ago—never mind how long precisely-having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people’s hats off—then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me.'; +const P2 = + 'There now is your insular city of the Manhattoes, belted round by wharves as Indian isles by coral reefs—commerce surrounds it with her surf. Right and left, the streets take you waterward. Its extreme downtown is the battery, where that noble mole is washed by waves, and cooled by breezes, which a few hours previous were out of sight of land. Look at the crowds of water-gazers there.'; describe('compose an epic', function() { it('load', function() { @@ -30,89 +33,102 @@ describe('compose an epic', function() { it('enter', function() { browser.keys(KEYS.Enter); - expect(browser.getHTML('.ql-editor', false)).toEqual([ - '

The Whale

', - '


' - ].join('')); + expect(browser.getHTML('.ql-editor', false)).toEqual( + ['

The Whale

', '


'].join(''), + ); }); it('tab', function() { browser.keys([KEYS.Enter, KEYS.Tab, P1, KEYS.Enter, KEYS.Enter, P2]); - expect(browser.getHTML('.ql-editor', false)).toEqual([ - '

The Whale

', - '


', - '

\t' + P1 + '

', - '


', - '

' + P2 + '

' - ].join('')); + expect(browser.getHTML('.ql-editor', false)).toEqual( + [ + '

The Whale

', + '


', + `

\t${P1}

`, + '


', + `

${P2}

`, + ].join(''), + ); }); it('arrow keys', function() { - browser.keys(Array(20).fill(KEYS.Up)) // More than enough to get to top - .keys(KEYS.Down) - .keys([KEYS.Enter, CHAPTER, KEYS.Enter]); - expect(browser.getHTML('.ql-editor', false)).toEqual([ - '

The Whale

', - '


', - '

' + CHAPTER + '

', - '


', - '

\t' + P1 + '

', - '


', - '

' + P2 + '

' - ].join('')); + browser + .keys(Array(20).fill(KEYS.Up)) // More than enough to get to top + .keys(KEYS.Down) + .keys([KEYS.Enter, CHAPTER, KEYS.Enter]); + expect(browser.getHTML('.ql-editor', false)).toEqual( + [ + '

The Whale

', + '


', + `

${CHAPTER}

`, + '


', + `

\t${P1}

`, + '


', + `

${P2}

`, + ].join(''), + ); }); it('backspace', function() { - browser.keys(Array(10).fill(KEYS.Up)) // More than enough to get to top - .keys(Array(4).fill(KEYS.Right)) - .keys(Array(4).fill(KEYS.Backspace)); - expect(browser.getHTML('.ql-editor', false)).toEqual([ - '

Whale

', - '


', - '

' + CHAPTER + '

', - '


', - '

\t' + P1 + '

', - '


', - '

' + P2 + '

' - ].join('')); + browser + .keys(Array(10).fill(KEYS.Up)) // More than enough to get to top + .keys(Array(4).fill(KEYS.Right)) + .keys(Array(4).fill(KEYS.Backspace)); + expect(browser.getHTML('.ql-editor', false)).toEqual( + [ + '

Whale

', + '


', + `

${CHAPTER}

`, + '


', + `

\t${P1}

`, + '


', + `

${P2}

`, + ].join(''), + ); }); it('delete', function() { browser.keys(Array(5).fill(KEYS.Delete)); - expect(browser.getHTML('.ql-editor', false)).toEqual([ - '


', - '


', - '

' + CHAPTER + '

', - '


', - '

\t' + P1 + '

', - '


', - '

' + P2 + '

' - ].join('')); + expect(browser.getHTML('.ql-editor', false)).toEqual( + [ + '


', + '


', + `

${CHAPTER}

`, + '


', + `

\t${P1}

`, + '


', + `

${P2}

`, + ].join(''), + ); }); it('delete newline', function() { browser.keys(KEYS.Delete); - expect(browser.getHTML('.ql-editor', false)).toEqual([ - '


', - '

' + CHAPTER + '

', - '


', - '

\t' + P1 + '

', - '


', - '

' + P2 + '

' - ].join('')); + expect(browser.getHTML('.ql-editor', false)).toEqual( + [ + '


', + `

${CHAPTER}

`, + '


', + `

\t${P1}

`, + '


', + `

${P2}

`, + ].join(''), + ); }); it('preformat', function() { browser.click('.ql-toolbar .ql-bold'); browser.click('.ql-toolbar .ql-italic'); - expect(browser.getHTML('.ql-editor', false)).toEqual([ - '

\uFEFF

', - '

' + CHAPTER + '

', - '


', - '

\t' + P1 + '

', - '


', - '

' + P2 + '

' - ].join('')); + expect(browser.getHTML('.ql-editor', false)).toEqual( + [ + '

\uFEFF

', + `

${CHAPTER}

`, + '


', + `

\t${P1}

`, + '


', + `

${P2}

`, + ].join(''), + ); expect(browser.isExisting('.ql-toolbar .ql-bold.ql-active')).toBe(true); }); @@ -121,48 +137,58 @@ describe('compose an epic', function() { browser.click('.ql-editor .ql-cursor'); } browser.keys('Moby Dick'); - expect(browser.getHTML('.ql-editor', false)).toEqual([ - '

Moby Dick

', - '

' + CHAPTER + '

', - '


', - '

\t' + P1 + '

', - '


', - '

' + P2 + '

' - ].join('')); + expect(browser.getHTML('.ql-editor', false)).toEqual( + [ + '

Moby Dick

', + `

${CHAPTER}

`, + '


', + `

\t${P1}

`, + '


', + `

${P2}

`, + ].join(''), + ); expect(browser.isExisting('.ql-toolbar .ql-bold.ql-active')).toBe(true); }); it('toolbar active', function() { - browser.keys([KEYS.Right, KEYS.Shift]) - .keys(Array(CHAPTER.length).fill(KEYS.Right)) - .keys(KEYS.Null); + browser + .keys([KEYS.Right, KEYS.Shift]) + .keys(Array(CHAPTER.length).fill(KEYS.Right)) + .keys(KEYS.Null); expect(browser.isExisting('.ql-toolbar .ql-bold.ql-active')).toBe(false); }); it('hotkey format', function() { browser.keys([KEYS.Short, 'b', KEYS.Null]); expect(browser.isExisting('.ql-toolbar .ql-bold.ql-active')).toBe(true); - expect(browser.getHTML('.ql-editor', false)).toEqual([ - '

Moby Dick

', - '

' + CHAPTER + '

', - '


', - '

\t' + P1 + '

', - '


', - '

' + P2 + '

' - ].join('')); + expect(browser.getHTML('.ql-editor', false)).toEqual( + [ + '

Moby Dick

', + `

${CHAPTER}

`, + '


', + `

\t${P1}

`, + '


', + `

${P2}

`, + ].join(''), + ); }); it('line format', function() { - browser.keys([KEYS.Left, KEYS.Up]) - .click('.ql-toolbar .ql-header[value="1"]'); - expect(browser.getHTML('.ql-editor', false)).toEqual([ - '

Moby Dick

', - '

' + CHAPTER + '

', - '


', - '

\t' + P1 + '

', - '


', - '

' + P2 + '

' - ].join('')); - expect(browser.isExisting('.ql-toolbar .ql-header.ql-active[value="1"]')).toBe(true); + browser + .keys([KEYS.Left, KEYS.Up]) + .click('.ql-toolbar .ql-header[value="1"]'); + expect(browser.getHTML('.ql-editor', false)).toEqual( + [ + '

Moby Dick

', + `

${CHAPTER}

`, + '


', + `

\t${P1}

`, + '


', + `

${P2}

`, + ].join(''), + ); + expect( + browser.isExisting('.ql-toolbar .ql-header.ql-active[value="1"]'), + ).toBe(true); }); }); diff --git a/test/helpers/unit.js b/test/helpers/unit.js index 01b2dbbd48..b6daf42919 100644 --- a/test/helpers/unit.js +++ b/test/helpers/unit.js @@ -1,29 +1,26 @@ +import equal from 'deep-equal'; import Editor from '../../core/editor'; import Emitter from '../../core/emitter'; import Selection from '../../core/selection'; import Scroll from '../../blots/scroll'; import Quill from '../../core/quill'; -import equal from 'deep-equal'; - -let div = document.createElement('div'); +const div = document.createElement('div'); div.id = 'test-container'; document.body.appendChild(div); - window.onerror = function(msg) { - return (msg === 'Script error.'); + return msg === 'Script error.'; }; - beforeEach(function() { jasmine.addMatchers({ - toEqualHTML: function() { - return { compare: compareHTML } + toEqualHTML() { + return { compare: compareHTML }; + }, + toBeApproximately() { + return { compare: compareApproximately }; }, - toBeApproximately: function() { - return { compare: compareApproximately } - } }); div.innerHTML = '
'; @@ -31,36 +28,34 @@ beforeEach(function() { this.initialize = initialize.bind(this); }); - function compareApproximately(actual, expected, tolerance) { - let pass = Math.abs(actual - expected) <= tolerance; + const pass = Math.abs(actual - expected) <= tolerance; return { - pass: pass, - message: `${actual} is ${(pass ? '' : 'not')} approximately ${expected}` + pass, + message: `${actual} is ${pass ? '' : 'not'} approximately ${expected}`, }; } function compareHTML(actual, expected, ignoreClassId) { - let [div1, div2] = [actual, expected].map(function(html) { + const [div1, div2] = [actual, expected].map(function(html) { if (html instanceof HTMLElement) { html = html.innerHTML; } - let div = document.createElement('div'); - div.innerHTML = html.replace(/\n\s*/g, ''); - return div; + const container = document.createElement('div'); + container.innerHTML = html.replace(/\n\s*/g, ''); + return container; }); let ignoredAttributes = ['width', 'height']; if (ignoreClassId) { ignoredAttributes = ignoredAttributes.concat(['class', 'id']); } - let message = compareNodes(div1, div2, ignoredAttributes) + const message = compareNodes(div1, div2, ignoredAttributes); if (message != null) { - console.error(div1.innerHTML); // eslint-disable-line no-console - console.error(div2.innerHTML); // eslint-disable-line no-console - return { pass: false, message: message }; - } else { - return { pass: true, message: 'HTMLs equal' }; + console.error(div1.innerHTML); // eslint-disable-line no-console + console.error(div2.innerHTML); // eslint-disable-line no-console + return { pass: false, message }; } + return { pass: true, message: 'HTMLs equal' }; } function compareNodes(node1, node2, ignoredAttributes = []) { @@ -71,26 +66,36 @@ function compareNodes(node1, node2, ignoredAttributes = []) { if (node1.tagName !== node2.tagName) { return `Expected tagName '${node1.tagName}' to equal '${node2.tagName}'`; } - let [attr1, attr2] = [node1, node2].map(function(node) { - return [].reduce.call(node.attributes || {}, function(attr, elem) { - if (ignoredAttributes.indexOf(elem.name) < 0) { - attr[elem.name] = elem.name === 'style' ? elem.value.trim() : elem.value; - } - return attr; - }, {}); + const [attr1, attr2] = [node1, node2].map(function(node) { + return [].reduce.call( + node.attributes || {}, + function(attr, elem) { + if (ignoredAttributes.indexOf(elem.name) < 0) { + attr[elem.name] = + elem.name === 'style' ? elem.value.trim() : elem.value; + } + return attr; + }, + {}, + ); }); if (!equal(attr1, attr2)) { - return `Expected attributes ${jasmine.pp(attr1)} to equal ${jasmine.pp(attr2)}`; + return `Expected attributes ${jasmine.pp(attr1)} to equal ${jasmine.pp( + attr2, + )}`; } if (node1.childNodes.length !== node2.childNodes.length) { - return `Expected node childNodes length '${node1.childNodes.length}' to equal '${node2.childNodes.length}'`; + return `Expected node childNodes length '${node1.childNodes + .length}' to equal '${node2.childNodes.length}'`; } if (node1.childNodes.length === 0) return null; let message = ''; - if ([].some.call(node1.childNodes, function(child1, i) { - message = compareNodes(child1, node2.childNodes[i], ignoredAttributes); - return message; - })) { + if ( + [].some.call(node1.childNodes, function(child1, i) { + message = compareNodes(child1, node2.childNodes[i], ignoredAttributes); + return message; + }) + ) { return message; } } else if (node1.data !== node2.data) { @@ -107,12 +112,13 @@ function initialize(klass, html, container = this.container) { } if (klass === HTMLElement) return container; if (klass === Quill) return new Quill(container); - let emitter = new Emitter(); - let scroll = new Scroll(container, { emitter: emitter }); + const emitter = new Emitter(); + const scroll = new Scroll(container, { emitter }); if (klass === Scroll) return scroll; if (klass === Editor) return new Editor(scroll); if (klass === Selection) return new Selection(scroll, emitter); if (klass[0] === Editor && klass[1] === Selection) { return [new Editor(scroll), new Selection(scroll, emitter)]; } + return null; } diff --git a/test/unit.js b/test/unit.js index 22b7a28aaf..3bd5c16634 100644 --- a/test/unit.js +++ b/test/unit.js @@ -1,14 +1,16 @@ +/* eslint-disable */ + import Quill from '../quill.js'; import CodeBlock from '../formats/code'; Quill.register(CodeBlock, true); // Syntax version will otherwise be registered -import './helpers/unit.js'; +import './helpers/unit'; -import './unit/blots/scroll.js'; -import './unit/blots/block.js'; -import './unit/blots/block-embed.js'; -import './unit/blots/inline.js'; +import './unit/blots/scroll'; +import './unit/blots/block'; +import './unit/blots/block-embed'; +import './unit/blots/inline'; import './unit/core/editor'; import './unit/core/selection'; @@ -33,5 +35,4 @@ import './unit/modules/toolbar'; import './unit/ui/picker'; import './unit/theme/base/tooltip'; - export default Quill; diff --git a/test/unit/blots/block-embed.js b/test/unit/blots/block-embed.js index 8b02fbd577..b6cc570bc7 100644 --- a/test/unit/blots/block-embed.js +++ b/test/unit/blots/block-embed.js @@ -1,9 +1,8 @@ import Scroll from '../../../blots/scroll'; - describe('Block Embed', function() { it('insert', function() { - let scroll = this.initialize(Scroll, '

0123

'); + const scroll = this.initialize(Scroll, '

0123

'); scroll.insertAt(2, 'video', '#'); expect(scroll.domNode).toEqualHTML(`

01

@@ -13,7 +12,7 @@ describe('Block Embed', function() { }); it('split newline', function() { - let scroll = this.initialize(Scroll, '

0123

'); + const scroll = this.initialize(Scroll, '

0123

'); scroll.insertAt(4, 'video', '#'); expect(scroll.domNode).toEqualHTML(`

0123

@@ -23,7 +22,7 @@ describe('Block Embed', function() { }); it('insert end of document', function() { - let scroll = this.initialize(Scroll, '

0123

'); + const scroll = this.initialize(Scroll, '

0123

'); scroll.insertAt(5, 'video', '#'); expect(scroll.domNode).toEqualHTML(`

0123

@@ -32,7 +31,10 @@ describe('Block Embed', function() { }); it('insert text before', function() { - let scroll = this.initialize(Scroll, ''); + const scroll = this.initialize( + Scroll, + '', + ); scroll.insertAt(0, 'Test'); expect(scroll.domNode).toEqualHTML(`

Test

@@ -41,7 +43,10 @@ describe('Block Embed', function() { }); it('insert text after', function() { - let scroll = this.initialize(Scroll, ''); + const scroll = this.initialize( + Scroll, + '', + ); scroll.insertAt(1, 'Test'); expect(scroll.domNode).toEqualHTML(` @@ -50,7 +55,10 @@ describe('Block Embed', function() { }); it('insert inline embed before', function() { - let scroll = this.initialize(Scroll, ''); + const scroll = this.initialize( + Scroll, + '', + ); scroll.insertAt(0, 'image', '/assets/favicon.png'); expect(scroll.domNode).toEqualHTML(`

@@ -59,7 +67,10 @@ describe('Block Embed', function() { }); it('insert inline embed after', function() { - let scroll = this.initialize(Scroll, ''); + const scroll = this.initialize( + Scroll, + '', + ); scroll.insertAt(1, 'image', '/assets/favicon.png'); expect(scroll.domNode).toEqualHTML(` @@ -68,7 +79,10 @@ describe('Block Embed', function() { }); it('insert block embed before', function() { - let scroll = this.initialize(Scroll, ''); + const scroll = this.initialize( + Scroll, + '', + ); scroll.insertAt(0, 'video', '#1'); expect(scroll.domNode).toEqualHTML(` @@ -77,7 +91,10 @@ describe('Block Embed', function() { }); it('insert block embed after', function() { - let scroll = this.initialize(Scroll, ''); + const scroll = this.initialize( + Scroll, + '', + ); scroll.insertAt(1, 'video', '#1'); expect(scroll.domNode).toEqualHTML(` @@ -86,7 +103,10 @@ describe('Block Embed', function() { }); it('insert newline before', function() { - let scroll = this.initialize(Scroll, ''); + const scroll = this.initialize( + Scroll, + '', + ); scroll.insertAt(0, '\n'); scroll.optimize(); expect(scroll.domNode).toEqualHTML(` @@ -96,7 +116,10 @@ describe('Block Embed', function() { }); it('insert newline after', function() { - let scroll = this.initialize(Scroll, ''); + const scroll = this.initialize( + Scroll, + '', + ); scroll.insertAt(1, '\n'); scroll.optimize(); expect(scroll.domNode).toEqualHTML(` @@ -106,7 +129,10 @@ describe('Block Embed', function() { }); it('delete preceding newline', function() { - let scroll = this.initialize(Scroll, '

0123

'); + const scroll = this.initialize( + Scroll, + '

0123

', + ); scroll.deleteAt(4, 1); expect(scroll.domNode).toEqualHTML(`

0123

diff --git a/test/unit/blots/block.js b/test/unit/blots/block.js index d2162e047c..5f2aaa09d2 100644 --- a/test/unit/blots/block.js +++ b/test/unit/blots/block.js @@ -1,28 +1,29 @@ import Parchment from 'parchment'; import Scroll from '../../../blots/scroll'; - describe('Block', function() { it('childless', function() { - let block = Parchment.create('block'); + const block = Parchment.create('block'); block.optimize(); expect(block.domNode).toEqualHTML('
'); }); it('insert into empty', function() { - let block = Parchment.create('block'); + const block = Parchment.create('block'); block.insertAt(0, 'Test'); expect(block.domNode).toEqualHTML('Test'); }); it('insert newlines', function() { - let scroll = this.initialize(Scroll, '


'); + const scroll = this.initialize(Scroll, '


'); scroll.insertAt(0, '\n\n\n'); - expect(scroll.domNode).toEqualHTML('





'); + expect(scroll.domNode).toEqualHTML( + '





', + ); }); it('insert multiline', function() { - let scroll = this.initialize(Scroll, '

Hello World!

'); + const scroll = this.initialize(Scroll, '

Hello World!

'); scroll.insertAt(6, 'pardon\nthis\n\ninterruption\n'); expect(scroll.domNode).toEqualHTML(`

Hello pardon

@@ -34,50 +35,53 @@ describe('Block', function() { }); it('insert into formatted', function() { - let scroll = this.initialize(Scroll, '

Welcome

'); + const scroll = this.initialize(Scroll, '

Welcome

'); scroll.insertAt(3, 'l\n'); expect(scroll.domNode.firstChild.outerHTML).toEqualHTML('

Well

'); expect(scroll.domNode.childNodes[1].outerHTML).toEqualHTML('

come

'); }); it('delete line contents', function() { - let scroll = this.initialize(Scroll, '

Hello

World!

'); + const scroll = this.initialize(Scroll, '

Hello

World!

'); scroll.deleteAt(0, 5); expect(scroll.domNode).toEqualHTML('


World!

'); }); it('join lines', function() { - let scroll = this.initialize(Scroll, '

Hello

World!

'); + const scroll = this.initialize(Scroll, '

Hello

World!

'); scroll.deleteAt(5, 1); expect(scroll.domNode).toEqualHTML('

HelloWorld!

'); }); it('join line with empty', function() { - let scroll = this.initialize(Scroll, '

HelloWorld


'); + const scroll = this.initialize( + Scroll, + '

HelloWorld


', + ); scroll.deleteAt(10, 1); expect(scroll.domNode).toEqualHTML('

HelloWorld

'); }); it('join empty lines', function() { - let scroll = this.initialize(Scroll, '



'); + const scroll = this.initialize(Scroll, '



'); scroll.deleteAt(1, 1); expect(scroll.domNode).toEqualHTML('


'); }); it('format empty', function() { - let scroll = this.initialize(Scroll, '


'); + const scroll = this.initialize(Scroll, '


'); scroll.formatAt(0, 1, 'header', 1); expect(scroll.domNode).toEqualHTML('


'); }); it('format newline', function() { - let scroll = this.initialize(Scroll, '

Hello

'); + const scroll = this.initialize(Scroll, '

Hello

'); scroll.formatAt(5, 1, 'header', 2); expect(scroll.domNode).toEqualHTML('

Hello

'); }); it('remove unnecessary break', function() { - let scroll = this.initialize(Scroll, '

Test

'); + const scroll = this.initialize(Scroll, '

Test

'); scroll.children.head.domNode.appendChild(document.createElement('br')); scroll.update(); expect(scroll.domNode).toEqualHTML('

Test

'); diff --git a/test/unit/blots/inline.js b/test/unit/blots/inline.js index f186bd2cd6..0b98d3d373 100644 --- a/test/unit/blots/inline.js +++ b/test/unit/blots/inline.js @@ -1,22 +1,21 @@ import Scroll from '../../../blots/scroll'; - describe('Inline', function() { it('format order', function() { - let scroll = this.initialize(Scroll, '

Hello World!

'); + const scroll = this.initialize(Scroll, '

Hello World!

'); scroll.formatAt(0, 1, 'bold', true); scroll.formatAt(0, 1, 'italic', true); scroll.formatAt(2, 1, 'italic', true); scroll.formatAt(2, 1, 'bold', true); expect(scroll.domNode).toEqualHTML( - '

Hello World!

' + '

Hello World!

', ); }); it('reorder', function() { - let scroll = this.initialize(Scroll, '

0123

'); - let p = scroll.domNode.firstChild; - let em = document.createElement('em'); + const scroll = this.initialize(Scroll, '

0123

'); + const p = scroll.domNode.firstChild; + const em = document.createElement('em'); [].slice.call(p.childNodes).forEach(function(node) { em.appendChild(node); }); @@ -24,7 +23,7 @@ describe('Inline', function() { expect(scroll.domNode).toEqualHTML('

0123

'); scroll.update(); expect(scroll.domNode).toEqualHTML( - '

0123

' + '

0123

', ); }); }); diff --git a/test/unit/blots/scroll.js b/test/unit/blots/scroll.js index f4bda62de1..50dc18cf48 100644 --- a/test/unit/blots/scroll.js +++ b/test/unit/blots/scroll.js @@ -1,76 +1,95 @@ -import Parchment from 'parchment'; -import Emitter from '../../../core/emitter'; -import Selection, { Range } from '../../../core/selection'; -import Cursor from '../../../blots/cursor'; -import Scroll from '../../../blots/scroll'; +import Parchment from "parchment"; +import Emitter from "../../../core/emitter"; +import Selection, { Range } from "../../../core/selection"; +import Cursor from "../../../blots/cursor"; +import Scroll from "../../../blots/scroll"; - -describe('Scroll', function() { - it('initialize empty document', function() { - let scroll = this.initialize(Scroll, ''); - expect(scroll.domNode).toEqualHTML('


'); +describe("Scroll", function() { + it("initialize empty document", function() { + const scroll = this.initialize(Scroll, ""); + expect(scroll.domNode).toEqualHTML("


"); }); - it('api change', function() { - let scroll = this.initialize(Scroll, '

Hello World!

'); - spyOn(scroll.emitter, 'emit').and.callThrough(); - scroll.insertAt(5, '!'); - expect(scroll.emitter.emit).toHaveBeenCalledWith(Emitter.events.SCROLL_OPTIMIZE, jasmine.any(Array), jasmine.any(Object)); + it("api change", function() { + const scroll = this.initialize(Scroll, "

Hello World!

"); + spyOn(scroll.emitter, "emit").and.callThrough(); + scroll.insertAt(5, "!"); + expect(scroll.emitter.emit).toHaveBeenCalledWith( + Emitter.events.SCROLL_OPTIMIZE, + jasmine.any(Array), + jasmine.any(Object) + ); }); - it('user change', function(done) { - let scroll = this.initialize(Scroll, '

Hello World!

'); - spyOn(scroll.emitter, 'emit').and.callThrough(); - scroll.domNode.firstChild.appendChild(document.createTextNode('!')); + it("user change", function(done) { + const scroll = this.initialize(Scroll, "

Hello World!

"); + spyOn(scroll.emitter, "emit").and.callThrough(); + scroll.domNode.firstChild.appendChild(document.createTextNode("!")); setTimeout(function() { - expect(scroll.emitter.emit).toHaveBeenCalledWith(Emitter.events.SCROLL_OPTIMIZE, jasmine.any(Array), jasmine.any(Object)); - expect(scroll.emitter.emit).toHaveBeenCalledWith(Emitter.events.SCROLL_UPDATE, Emitter.sources.USER, jasmine.any(Array)); + expect(scroll.emitter.emit).toHaveBeenCalledWith( + Emitter.events.SCROLL_OPTIMIZE, + jasmine.any(Array), + jasmine.any(Object) + ); + expect(scroll.emitter.emit).toHaveBeenCalledWith( + Emitter.events.SCROLL_UPDATE, + Emitter.sources.USER, + jasmine.any(Array) + ); done(); }, 1); }); - it('whitelist', function() { - let scroll = Parchment.create('scroll', { emitter: new Emitter(), whitelist: ['bold'] }); - scroll.insertAt(0, 'Hello World!'); - scroll.formatAt(0, 5, 'bold', true); - scroll.formatAt(6, 5, 'italic', true); - expect(scroll.domNode.firstChild).toEqualHTML('Hello World!'); + it("whitelist", function() { + const scroll = Parchment.create("scroll", { + emitter: new Emitter(), + whitelist: ["bold"] + }); + scroll.insertAt(0, "Hello World!"); + scroll.formatAt(0, 5, "bold", true); + scroll.formatAt(6, 5, "italic", true); + expect(scroll.domNode.firstChild).toEqualHTML( + "Hello World!" + ); }); - describe('leaf()', function() { - it('text', function() { - let scroll = this.initialize(Scroll, '

Tests

'); - let [leaf, offset] = scroll.leaf(2); - expect(leaf.value()).toEqual('Tests'); + describe("leaf()", function() { + it("text", function() { + const scroll = this.initialize(Scroll, "

Tests

"); + const [leaf, offset] = scroll.leaf(2); + expect(leaf.value()).toEqual("Tests"); expect(offset).toEqual(2); }); - it('precise', function() { - let scroll = this.initialize(Scroll, '

01234

'); - let [leaf, offset] = scroll.leaf(3); - expect(leaf.value()).toEqual('2'); + it("precise", function() { + const scroll = this.initialize( + Scroll, + "

01234

" + ); + const [leaf, offset] = scroll.leaf(3); + expect(leaf.value()).toEqual("2"); expect(offset).toEqual(1); }); - it('newline', function() { - let scroll = this.initialize(Scroll, '

0123

5678

'); - let [leaf, offset] = scroll.leaf(4); - expect(leaf.value()).toEqual('0123'); + it("newline", function() { + const scroll = this.initialize(Scroll, "

0123

5678

"); + const [leaf, offset] = scroll.leaf(4); + expect(leaf.value()).toEqual("0123"); expect(offset).toEqual(4); }); - it('cursor', function() { - let selection = this.initialize(Selection, '

012

'); + it("cursor", function() { + const selection = this.initialize(Selection, "

012

"); selection.setRange(new Range(2)); - selection.format('strike', true); - let [leaf, offset] = selection.scroll.leaf(2); + selection.format("strike", true); + const [leaf, offset] = selection.scroll.leaf(2); expect(leaf instanceof Cursor).toBe(true); expect(offset).toEqual(0); }); - it('beyond document', function() { - let scroll = this.initialize(Scroll, '

Test

'); - let [leaf, offset] = scroll.leaf(10); + it("beyond document", function() { + const scroll = this.initialize(Scroll, "

Test

"); + const [leaf, offset] = scroll.leaf(10); expect(leaf).toEqual(null); expect(offset).toEqual(-1); }); diff --git a/test/unit/core/editor.js b/test/unit/core/editor.js index 3518e8e2b5..597f1d402d 100644 --- a/test/unit/core/editor.js +++ b/test/unit/core/editor.js @@ -2,201 +2,218 @@ import Delta from 'quill-delta'; import Editor from '../../../core/editor'; import Selection, { Range } from '../../../core/selection'; - describe('Editor', function() { describe('insert', function() { it('text', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.insertText(2, '!!'); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01!!23', { bold: true }) - .insert('\n') + expect(editor.getDelta()).toEqual( + new Delta().insert('01!!23', { bold: true }).insert('\n'), ); expect(this.container).toEqualHTML('

01!!23

'); }); it('embed', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.insertEmbed(2, 'image', '/assets/favicon.png'); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01', { bold: true }) - .insert({ image: '/assets/favicon.png'}, { bold: true }) - .insert('23', { bold: true }) - .insert('\n') + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01', { bold: true }) + .insert({ image: '/assets/favicon.png' }, { bold: true }) + .insert('23', { bold: true }) + .insert('\n'), + ); + expect(this.container).toEqualHTML( + '

0123

', ); - expect(this.container).toEqualHTML('

0123

'); }); it('on empty line', function() { - let editor = this.initialize(Editor, '

0


3

'); + const editor = this.initialize(Editor, '

0


3

'); editor.insertText(2, '!'); expect(editor.getDelta()).toEqual(new Delta().insert('0\n!\n3\n')); expect(this.container).toEqualHTML('

0

!

3

'); }); it('end of document', function() { - let editor = this.initialize(Editor, '

Hello

'); + const editor = this.initialize(Editor, '

Hello

'); editor.insertText(6, 'World!'); expect(editor.getDelta()).toEqual(new Delta().insert('Hello\nWorld!\n')); expect(this.container).toEqualHTML('

Hello

World!

'); }); it('end of document with newline', function() { - let editor = this.initialize(Editor, '

Hello

'); + const editor = this.initialize(Editor, '

Hello

'); editor.insertText(6, 'World!\n'); expect(editor.getDelta()).toEqual(new Delta().insert('Hello\nWorld!\n')); expect(this.container).toEqualHTML('

Hello

World!

'); }); it('embed at end of document with newline', function() { - let editor = this.initialize(Editor, '

Hello

'); + const editor = this.initialize(Editor, '

Hello

'); editor.insertEmbed(6, 'image', '/assets/favicon.png'); - expect(editor.getDelta()).toEqual(new Delta() - .insert('Hello\n') - .insert({ image: '/assets/favicon.png' }) - .insert('\n')); - expect(this.container).toEqualHTML('

Hello

'); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('Hello\n') + .insert({ image: '/assets/favicon.png' }) + .insert('\n'), + ); + expect(this.container).toEqualHTML( + '

Hello

', + ); }); it('newline splitting', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.insertText(2, '\n'); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01', { bold: true }) - .insert('\n') - .insert('23', { bold: true }) - .insert('\n') + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01', { bold: true }) + .insert('\n') + .insert('23', { bold: true }) + .insert('\n'), ); expect(this.container).toEqualHTML(`

01

-

23

` - ); +

23

`); }); it('prepend newline', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.insertText(0, '\n'); - expect(editor.getDelta()).toEqual(new Delta() - .insert('\n') - .insert('0123', { bold: true }) - .insert('\n') + expect(editor.getDelta()).toEqual( + new Delta() + .insert('\n') + .insert('0123', { bold: true }) + .insert('\n'), ); expect(this.container).toEqualHTML(`


-

0123

` - ); +

0123

`); }); it('append newline', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.insertText(4, '\n'); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0123', { bold: true }) - .insert('\n\n') + expect(editor.getDelta()).toEqual( + new Delta().insert('0123', { bold: true }).insert('\n\n'), ); expect(this.container).toEqualHTML(`

0123

-


` - ); +


`); }); it('multiline text', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.insertText(2, '\n!!\n!!\n'); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01', { bold: true }) - .insert('\n') - .insert('!!', { bold: true }) - .insert('\n') - .insert('!!', { bold: true }) - .insert('\n') - .insert('23', { bold: true }) - .insert('\n')); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01', { bold: true }) + .insert('\n') + .insert('!!', { bold: true }) + .insert('\n') + .insert('!!', { bold: true }) + .insert('\n') + .insert('23', { bold: true }) + .insert('\n'), + ); expect(this.container).toEqualHTML(`

01

!!

!!

-

23

` - ); +

23

`); }); it('multiple newlines', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.insertText(2, '\n\n'); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01', { bold: true }) - .insert('\n\n') - .insert('23', { bold: true }) - .insert('\n') + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01', { bold: true }) + .insert('\n\n') + .insert('23', { bold: true }) + .insert('\n'), ); expect(this.container).toEqualHTML(`

01


-

23

` - ); +

23

`); }); it('text removing formatting', function() { - let editor = this.initialize(Editor, '

01

'); + const editor = this.initialize(Editor, '

01

'); editor.insertText(2, '23', { bold: false, strike: false }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01', { strike: true }) - .insert('23\n') + expect(editor.getDelta()).toEqual( + new Delta().insert('01', { strike: true }).insert('23\n'), ); }); }); describe('delete', function() { it('inner node', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize( + Editor, + '

0123

', + ); editor.deleteText(1, 2); - expect(editor.getDelta()).toEqual(new Delta() - .insert('03', { bold: true, italic: true }) - .insert('\n') + expect(editor.getDelta()).toEqual( + new Delta().insert('03', { bold: true, italic: true }).insert('\n'), ); expect(this.container).toEqualHTML('

03

'); }); it('parts of multiple lines', function() { - let editor = this.initialize(Editor, '

0123

5678

'); + const editor = this.initialize( + Editor, + '

0123

5678

', + ); editor.deleteText(2, 5); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0178', { italic: true }) - .insert('\n') + expect(editor.getDelta()).toEqual( + new Delta().insert('0178', { italic: true }).insert('\n'), ); expect(this.container).toEqualHTML('

0178

'); }); it('entire line keeping newline', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize( + Editor, + '

0123

', + ); editor.deleteText(0, 4); expect(editor.getDelta()).toEqual(new Delta().insert('\n')); expect(this.container).toEqualHTML('


'); }); it('newline', function() { - let editor = this.initialize(Editor, '

0123

5678

'); + const editor = this.initialize( + Editor, + '

0123

5678

', + ); editor.deleteText(4, 1); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01235678', { italic: true }) - .insert('\n') + expect(editor.getDelta()).toEqual( + new Delta().insert('01235678', { italic: true }).insert('\n'), ); expect(this.container).toEqualHTML('

01235678

'); }); it('entire document', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize( + Editor, + '

0123

', + ); editor.deleteText(0, 5); expect(editor.getDelta()).toEqual(new Delta().insert('\n')); expect(this.container).toEqualHTML('


'); }); it('multiple complete lines', function() { - let editor = this.initialize(Editor, '

012

456

890

'); + const editor = this.initialize( + Editor, + '

012

456

890

', + ); editor.deleteText(0, 8); - expect(editor.getDelta()).toEqual(new Delta() - .insert('890', { italic: true }) - .insert('\n') + expect(editor.getDelta()).toEqual( + new Delta().insert('890', { italic: true }).insert('\n'), ); expect(this.container).toEqualHTML('

890

'); }); @@ -204,7 +221,7 @@ describe('Editor', function() { describe('format', function() { it('line', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.formatLine(1, 1, { header: 1 }); expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); @@ -212,38 +229,52 @@ describe('Editor', function() { describe('removeFormat', function() { it('unwrap', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.removeFormat(1, 2); expect(this.container).toEqualHTML('

0123

'); }); it('split inline', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize( + Editor, + '

0123

', + ); editor.removeFormat(1, 1); - expect(this.container).toEqualHTML('

0123

'); + expect(this.container).toEqualHTML( + '

0123

', + ); }); it('partial line', function() { - let editor = this.initialize(Editor, '
  1. 34
'); + const editor = this.initialize( + Editor, + '
  1. 34
', + ); editor.removeFormat(1, 3); expect(this.container).toEqualHTML('

01

34

'); }); it('remove embed', function() { - let editor = this.initialize(Editor, '

02

') + const editor = this.initialize( + Editor, + '

02

', + ); editor.removeFormat(1, 1); expect(this.container).toEqualHTML('

02

'); }); it('combined', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `
  1. 567890
- `); + `, + ); editor.removeFormat(1, 7); expect(this.container).toEqualHTML(`

013

@@ -252,12 +283,15 @@ describe('Editor', function() { }); it('end of document', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + ` `, ); selection.setNativeRange( - this.container.querySelector('em').firstChild, 1, - this.container.querySelector('u').firstChild, 1 + this.container.querySelector('em').firstChild, + 1, + this.container.querySelector('u').firstChild, + 1, ); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range.index).toEqual(1); expect(range.length).toEqual(3); }); it('between embed', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `

@@ -105,109 +122,140 @@ describe('Selection', function() { - ` + `, + ); + selection.setNativeRange( + this.container.firstChild, + 1, + this.container.lastChild.lastChild, + 1, ); - selection.setNativeRange(this.container.firstChild, 1, this.container.lastChild.lastChild, 1); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range.index).toEqual(1); expect(range.length).toEqual(3); }); it('between inlines', function() { - let selection = this.initialize(Selection, '

012345

'); - selection.setNativeRange(this.container.firstChild, 1, this.container.firstChild, 2); - let [range, ] = selection.getRange(); + const selection = this.initialize( + Selection, + '

012345

', + ); + selection.setNativeRange( + this.container.firstChild, + 1, + this.container.firstChild, + 2, + ); + const [range] = selection.getRange(); expect(range.index).toEqual(2); expect(range.length).toEqual(2); }); it('between blocks', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `

01


` + `, ); selection.setNativeRange(this.container, 1, this.container.lastChild, 1); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range.index).toEqual(3); expect(range.length).toEqual(4); }); it('wrong input', function() { - let container = this.initialize(HTMLElement, ` + const container = this.initialize( + HTMLElement, + ` -
` +
`, + ); + const selection = this.initialize( + Selection, + '

0123

', + container.lastChild, ); - let selection = this.initialize(Selection, '

0123

', container.lastChild); container.firstChild.select(); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range).toEqual(null); }); }); describe('setRange()', function() { it('empty document', function() { - let selection = this.initialize(Selection, ''); - let expected = new Range(0); + const selection = this.initialize(Selection, ''); + const expected = new Range(0); selection.setRange(expected); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range).toEqual(expected); expect(selection.hasFocus()).toBe(true); }); it('empty lines', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `


` + `, ); - let expected = new Range(0, 1); + const expected = new Range(0, 1); selection.setRange(expected); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range).toEqual(range); expect(selection.hasFocus()).toBe(true); }); it('nested text node', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `

01

` + `, ); - let expected = new Range(1, 3); + const expected = new Range(1, 3); selection.setRange(expected); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range).toEqual(expected); expect(selection.hasFocus()).toBe(true); }); it('between inlines', function() { - let selection = this.initialize(Selection, '

012345

'); - let expected = new Range(2, 2); + const selection = this.initialize( + Selection, + '

012345

', + ); + const expected = new Range(2, 2); selection.setRange(expected); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range).toEqual(expected); expect(selection.hasFocus()).toBe(true); }); it('single embed', function() { - let selection = this.initialize(Selection, - `

` + const selection = this.initialize( + Selection, + `

`, ); - let expected = new Range(1, 0); + const expected = new Range(1, 0); selection.setRange(expected); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range).toEqual(expected); expect(selection.hasFocus()).toBe(true); }); it('between embeds', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `

@@ -217,22 +265,22 @@ describe('Selection', function() { - ` + `, ); - let expected = new Range(1, 3); + const expected = new Range(1, 3); selection.setRange(expected); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range).toEqual(expected); expect(selection.hasFocus()).toBe(true); }); it('null', function() { - let selection = this.initialize(Selection, '

0123

'); + const selection = this.initialize(Selection, '

0123

'); selection.setRange(new Range(1)); - let [range, ] = selection.getRange(); + let [range] = selection.getRange(); expect(range).not.toEqual(null); selection.setRange(null); - [range, ] = selection.getRange(); + [range] = selection.getRange(); expect(range).toEqual(null); expect(selection.hasFocus()).toBe(false); }); @@ -283,7 +331,7 @@ describe('Selection', function() { this.setup(`

0123

`, 2); this.selection.format('underline', true); this.selection.scroll.update(); - let native = this.selection.getNativeRange(); + const native = this.selection.getNativeRange(); expect(native.start.node).toEqual(this.selection.cursor.textNode); }); @@ -331,7 +379,7 @@ describe('Selection', function() { it('text change cleanup', function() { this.setup(`

0123

`, 2); this.selection.format('italic', true); - this.selection.cursor.textNode.data = Cursor.CONTENTS + '|'; + this.selection.cursor.textNode.data = `${Cursor.CONTENTS}|`; this.selection.setNativeRange(this.selection.cursor.textNode, 2); this.selection.scroll.update(); expect(this.container).toEqualHTML('

01|23

'); @@ -353,7 +401,9 @@ describe('Selection', function() { beforeEach(function() { this.container.classList.add('ql-editor'); this.container.style.fontFamily = 'monospace'; - this.container.style.lineHeight = /Trident/i.test(navigator.userAgent) ? '18px' : 'initial'; + this.container.style.lineHeight = /Trident/i.test(navigator.userAgent) + ? '18px' + : 'initial'; this.initialize(HTMLElement, '
 
'); this.div = this.container.firstChild; this.div.style.border = '1px solid #777'; @@ -364,119 +414,162 @@ describe('Selection', function() { this.float.style.width = '1px'; if (this.reference != null) return; this.initialize(HTMLElement, '

0

', this.div); - let span = this.div.firstChild.firstChild; - span.style.display = 'inline-block'; // IE11 needs this to respect line height - let bounds = span.getBoundingClientRect(); + const span = this.div.firstChild.firstChild; + span.style.display = 'inline-block'; // IE11 needs this to respect line height + const bounds = span.getBoundingClientRect(); this.reference = { height: bounds.height, left: bounds.left, lineHeight: span.parentNode.offsetHeight, width: bounds.width, - top: bounds.top + top: bounds.top, }; this.initialize(HTMLElement, '', this.div); }); afterEach(function() { - this.float.style.left = this.bounds.left + 'px'; - this.float.style.top = this.bounds.top + 'px'; - this.float.style.height = this.bounds.height + 'px'; + this.float.style.left = `${this.bounds.left}px`; + this.float.style.top = `${this.bounds.top}px`; + this.float.style.height = `${this.bounds.height}px`; }); it('empty document', function() { - let selection = this.initialize(Selection, '


', this.div); + const selection = this.initialize(Selection, '


', this.div); this.bounds = selection.getBounds(0); - if (/Android/i.test(navigator.userAgent)) return; // false positive on emulators atm + if (/Android/i.test(navigator.userAgent)) return; // false positive on emulators atm expect(this.bounds.left).toBeApproximately(this.reference.left, 1); expect(this.bounds.height).toBeApproximately(this.reference.height, 1); expect(this.bounds.top).toBeApproximately(this.reference.top, 1); }); it('empty line', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `

0000


-

0000

` - , this.div); +

0000

`, + this.div, + ); this.bounds = selection.getBounds(5); - if (/Android/i.test(navigator.userAgent)) return; // false positive on emulators atm + if (/Android/i.test(navigator.userAgent)) return; // false positive on emulators atm expect(this.bounds.left).toBeApproximately(this.reference.left, 1); expect(this.bounds.height).toBeApproximately(this.reference.height, 1); - expect(this.bounds.top).toBeApproximately(this.reference.top + this.reference.lineHeight, 2); + expect(this.bounds.top).toBeApproximately( + this.reference.top + this.reference.lineHeight, + 2, + ); }); it('plain text', function() { - let selection = this.initialize(Selection, '

0123

', this.div); + const selection = this.initialize(Selection, '

0123

', this.div); this.bounds = selection.getBounds(2); - expect(this.bounds.left).toBeApproximately(this.reference.left + this.reference.width * 2, 2); + expect(this.bounds.left).toBeApproximately( + this.reference.left + this.reference.width * 2, + 2, + ); expect(this.bounds.height).toBeApproximately(this.reference.height, 1); expect(this.bounds.top).toBeApproximately(this.reference.top, 1); }); it('multiple characters', function() { - let selection = this.initialize(Selection, '

0123

', this.div); + const selection = this.initialize(Selection, '

0123

', this.div); this.bounds = selection.getBounds(1, 2); - expect(this.bounds.left).toBeApproximately(this.reference.left + this.reference.width, 2); + expect(this.bounds.left).toBeApproximately( + this.reference.left + this.reference.width, + 2, + ); expect(this.bounds.height).toBeApproximately(this.reference.height, 1); expect(this.bounds.top).toBeApproximately(this.reference.top, 1); - expect(this.bounds.width).toBeApproximately(this.reference.width*2, 2); + expect(this.bounds.width).toBeApproximately(this.reference.width * 2, 2); }); it('start of line', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `

0000

-

0000

` - , this.div); +

0000

`, + this.div, + ); this.bounds = selection.getBounds(5); expect(this.bounds.left).toBeApproximately(this.reference.left, 1); expect(this.bounds.height).toBeApproximately(this.reference.height, 1); - expect(this.bounds.top).toBeApproximately(this.reference.top + this.reference.lineHeight, 1); + expect(this.bounds.top).toBeApproximately( + this.reference.top + this.reference.lineHeight, + 1, + ); }); it('end of line', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `

0000

0000

-

0000

` - , this.div); +

0000

`, + this.div, + ); this.bounds = selection.getBounds(9); - expect(this.bounds.left).toBeApproximately(this.reference.left + this.reference.width * 4, 4); + expect(this.bounds.left).toBeApproximately( + this.reference.left + this.reference.width * 4, + 4, + ); expect(this.bounds.height).toBeApproximately(this.reference.height, 1); - expect(this.bounds.top).toBeApproximately(this.reference.top + this.reference.lineHeight, 1); + expect(this.bounds.top).toBeApproximately( + this.reference.top + this.reference.lineHeight, + 1, + ); }); it('multiple lines', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `

0000

0000

-

0000

` - , this.div); +

0000

`, + this.div, + ); this.bounds = selection.getBounds(2, 4); expect(this.bounds.left).toBeApproximately(this.reference.left, 1); - expect(this.bounds.height).toBeApproximately(this.reference.height*2, 2); + expect(this.bounds.height).toBeApproximately( + this.reference.height * 2, + 2, + ); expect(this.bounds.top).toBeApproximately(this.reference.top, 1); - expect(this.bounds.width).toBeGreaterThan(3*this.reference.width); + expect(this.bounds.width).toBeGreaterThan(3 * this.reference.width); }); it('large text', function() { - let selection = this.initialize(Selection, '

0000

', this.div); - let span = this.div.querySelector('span'); + const selection = this.initialize( + Selection, + '

0000

', + this.div, + ); + const span = this.div.querySelector('span'); if (/Trident/i.test(navigator.userAgent)) { span.style.lineHeight = '27px'; } this.bounds = selection.getBounds(2); - expect(this.bounds.left).toBeApproximately(this.reference.left + span.offsetWidth / 2, 1); + expect(this.bounds.left).toBeApproximately( + this.reference.left + span.offsetWidth / 2, + 1, + ); expect(this.bounds.height).toBeApproximately(span.offsetHeight, 1); expect(this.bounds.top).toBeApproximately(this.reference.top, 1); }); it('image', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `

-

` - , this.div); +

`, + this.div, + ); this.bounds = selection.getBounds(1); expect(this.bounds.left).toBeApproximately(this.reference.left + 32, 1); expect(this.bounds.height).toBeApproximately(32, 1); @@ -484,7 +577,7 @@ describe('Selection', function() { }); it('beyond document', function() { - let selection = this.initialize(Selection, '

0123

'); + const selection = this.initialize(Selection, '

0123

'); expect(() => { this.bounds = selection.getBounds(10, 0); }).not.toThrow(); diff --git a/test/unit/formats/align.js b/test/unit/formats/align.js index ccdb9523e9..0571ea402d 100644 --- a/test/unit/formats/align.js +++ b/test/unit/formats/align.js @@ -1,33 +1,44 @@ import Delta from 'quill-delta'; import Editor from '../../../core/editor'; - describe('Align', function() { it('add', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.formatText(4, 1, { align: 'center' }); - expect(editor.getDelta()).toEqual(new Delta().insert('0123').insert('\n', { align: 'center' })); - expect(editor.scroll.domNode).toEqualHTML('

0123

'); + expect(editor.getDelta()).toEqual( + new Delta().insert('0123').insert('\n', { align: 'center' }), + ); + expect(editor.scroll.domNode).toEqualHTML( + '

0123

', + ); }); it('remove', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize( + Editor, + '

0123

', + ); editor.formatText(4, 1, { align: false }); expect(editor.getDelta()).toEqual(new Delta().insert('0123\n')); expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('whitelist', function() { - let editor = this.initialize(Editor, '

0123

') - let initial = editor.scroll.domNode.innerHTML; + const editor = this.initialize( + Editor, + '

0123

', + ); + const initial = editor.scroll.domNode.innerHTML; editor.formatText(4, 1, { align: 'middle' }); - expect(editor.getDelta()).toEqual(new Delta().insert('0123').insert('\n', { align: 'center' })); + expect(editor.getDelta()).toEqual( + new Delta().insert('0123').insert('\n', { align: 'center' }), + ); expect(editor.scroll.domNode).toEqualHTML(initial); }); it('invalid scope', function() { - let editor = this.initialize(Editor, '

0123

'); - let initial = editor.scroll.domNode.innerHTML; + const editor = this.initialize(Editor, '

0123

'); + const initial = editor.scroll.domNode.innerHTML; editor.formatText(1, 2, { align: 'center' }); expect(editor.getDelta()).toEqual(new Delta().insert('0123\n')); expect(editor.scroll.domNode).toEqualHTML(initial); diff --git a/test/unit/formats/bold.js b/test/unit/formats/bold.js index 1f08d39594..f8f0bd88c9 100644 --- a/test/unit/formats/bold.js +++ b/test/unit/formats/bold.js @@ -1,12 +1,17 @@ import Scroll from '../../../blots/scroll'; - describe('Bold', function() { it('optimize and merge', function() { - let scroll = this.initialize(Scroll, '

abc

'); - let bold = document.createElement('b'); + const scroll = this.initialize( + Scroll, + '

abc

', + ); + const bold = document.createElement('b'); bold.appendChild(scroll.domNode.firstChild.childNodes[1]); - scroll.domNode.firstChild.insertBefore(bold, scroll.domNode.firstChild.lastChild); + scroll.domNode.firstChild.insertBefore( + bold, + scroll.domNode.firstChild.lastChild, + ); scroll.update(); expect(scroll.domNode).toEqualHTML('

abc

'); }); diff --git a/test/unit/formats/code.js b/test/unit/formats/code.js index ef7c162aaa..972dde5d48 100644 --- a/test/unit/formats/code.js +++ b/test/unit/formats/code.js @@ -1,17 +1,19 @@ import Delta from 'quill-delta'; import Editor from '../../../core/editor'; - describe('Code', function() { it('newline', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `

       


\n


\n\n


- `); + `, + ); expect(editor.scroll.domNode).toEqualHTML(`
\n


@@ -23,13 +25,17 @@ describe('Code', function() { }); it('default child', function() { - let editor = this.initialize(Editor, '


'); + const editor = this.initialize(Editor, '


'); editor.formatLine(0, 1, { 'code-block': true }); - expect(editor.scroll.domNode.innerHTML).toEqual('
\n
'); + expect(editor.scroll.domNode.innerHTML).toEqual( + '
\n
', + ); }); it('merge', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `
0
0


@@ -56,10 +62,11 @@ describe('Code', function() {


2\n\n
2\n\n
- `); + `, + ); editor.scroll.lines().forEach(function(line) { line.optimize(); - }) + }); expect(editor.scroll.domNode).toEqualHTML(`
0\n0\n


@@ -82,12 +89,15 @@ describe('Code', function() { }); it('merge multiple', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `
0
1
2
3
- `); + `, + ); editor.scroll.children.head.optimize(); expect(editor.scroll.domNode).toEqualHTML(`
0\n1\n2\n3\n
@@ -95,150 +105,221 @@ describe('Code', function() { }); it('add', function() { - let editor = this.initialize(Editor, '

0123

5678

'); + const editor = this.initialize(Editor, '

0123

5678

'); editor.formatLine(2, 5, { 'code-block': true }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0123').insert('\n', { 'code-block': true }) - .insert('5678').insert('\n', { 'code-block': true }) + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0123') + .insert('\n', { 'code-block': true }) + .insert('5678') + .insert('\n', { 'code-block': true }), + ); + expect(editor.scroll.domNode.innerHTML).toEqual( + '
0123\n5678\n
', ); - expect(editor.scroll.domNode.innerHTML).toEqual('
0123\n5678\n
'); }); it('remove', function() { - let editor = this.initialize(Editor, { html: '
0123\n
' }); + const editor = this.initialize(Editor, { html: '
0123\n
' }); editor.formatText(4, 1, { 'code-block': false }); expect(editor.getDelta()).toEqual(new Delta().insert('0123\n')); expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('delete last', function() { - let editor = this.initialize(Editor, { html: '

0123

\n

5678

' }); + const editor = this.initialize(Editor, { + html: '

0123

\n

5678

', + }); editor.deleteText(4, 1); - expect(editor.getDelta()).toEqual(new Delta().insert('0123').insert('\n', { 'code-block': true }).insert('5678\n')); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0123') + .insert('\n', { 'code-block': true }) + .insert('5678\n'), + ); expect(editor.scroll.domNode).toEqualHTML('
0123

5678

'); }); it('delete merge before', function() { - let editor = this.initialize(Editor, { html: '

0123

4567\n
' }); + const editor = this.initialize(Editor, { + html: '

0123

4567\n
', + }); editor.deleteText(4, 1); - expect(editor.getDelta()).toEqual(new Delta().insert('01234567').insert('\n', { 'code-block': true })); + expect(editor.getDelta()).toEqual( + new Delta().insert('01234567').insert('\n', { 'code-block': true }), + ); expect(editor.scroll.domNode).toEqualHTML('
01234567\n
'); }); it('delete merge after', function() { - let editor = this.initialize(Editor, { html: '
0123\n

4567

' }); + const editor = this.initialize(Editor, { + html: '
0123\n

4567

', + }); editor.deleteText(4, 1); - expect(editor.getDelta()).toEqual(new Delta().insert('01234567').insert('\n', { header: 1 })); + expect(editor.getDelta()).toEqual( + new Delta().insert('01234567').insert('\n', { header: 1 }), + ); expect(editor.scroll.domNode).toEqualHTML('

01234567

'); }); it('delete across before partial merge', function() { - let editor = this.initialize(Editor, { html: '
01\n34\n67\n

90

' }); + const editor = this.initialize(Editor, { + html: '
01\n34\n67\n

90

', + }); editor.deleteText(7, 3); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01').insert('\n', { 'code-block': true }) - .insert('34').insert('\n', { 'code-block': true }) - .insert('60').insert('\n', { header: 1 }) + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01') + .insert('\n', { 'code-block': true }) + .insert('34') + .insert('\n', { 'code-block': true }) + .insert('60') + .insert('\n', { header: 1 }), + ); + expect(editor.scroll.domNode.innerHTML).toEqualHTML( + '
01\n34\n

60

', ); - expect(editor.scroll.domNode.innerHTML).toEqualHTML('
01\n34\n

60

'); }); it('delete across before no merge', function() { - let editor = this.initialize(Editor, { html: '
01\n34\n

6789

' }); + const editor = this.initialize(Editor, { + html: '
01\n34\n

6789

', + }); editor.deleteText(3, 5); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01').insert('\n', { 'code-block': true }) - .insert('89').insert('\n', { header: 1 }) + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01') + .insert('\n', { 'code-block': true }) + .insert('89') + .insert('\n', { header: 1 }), + ); + expect(editor.scroll.domNode.innerHTML).toEqualHTML( + '
01\n

89

', ); - expect(editor.scroll.domNode.innerHTML).toEqualHTML('
01\n

89

'); }); it('delete across after', function() { - let editor = this.initialize(Editor, { html: '

0123

56\n89\n
' }); + const editor = this.initialize(Editor, { + html: '

0123

56\n89\n
', + }); editor.deleteText(2, 4); - expect(editor.getDelta()).toEqual(new Delta() - .insert('016').insert('\n', { 'code-block': true }) - .insert('89').insert('\n', { 'code-block': true }) + expect(editor.getDelta()).toEqual( + new Delta() + .insert('016') + .insert('\n', { 'code-block': true }) + .insert('89') + .insert('\n', { 'code-block': true }), ); expect(editor.scroll.domNode.innerHTML).toEqualHTML('
016\n89\n
'); }); it('replace', function() { - let editor = this.initialize(Editor, { html: '
0123\n
' }); - editor.formatText(4, 1, { 'header': 1 }); - expect(editor.getDelta()).toEqual(new Delta().insert('0123').insert('\n', { header: 1 })); + const editor = this.initialize(Editor, { html: '
0123\n
' }); + editor.formatText(4, 1, { header: 1 }); + expect(editor.getDelta()).toEqual( + new Delta().insert('0123').insert('\n', { header: 1 }), + ); expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('replace multiple', function() { - let editor = this.initialize(Editor, { html: '
01\n23\n
' }); - editor.formatText(0, 6, { 'header': 1 }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01').insert('\n', { header: 1 }) - .insert('23').insert('\n', { header: 1 }) + const editor = this.initialize(Editor, { html: '
01\n23\n
' }); + editor.formatText(0, 6, { header: 1 }); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01') + .insert('\n', { header: 1 }) + .insert('23') + .insert('\n', { header: 1 }), ); expect(editor.scroll.domNode).toEqualHTML('

01

23

'); }); it('format interior line', function() { - let editor = this.initialize(Editor, { html: '
01\n23\n45\n
' }); - editor.formatText(5, 1, { 'header': 1 }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01').insert('\n', { 'code-block': true }) - .insert('23').insert('\n', { 'header': 1 }) - .insert('45').insert('\n', { 'code-block': true }) + const editor = this.initialize(Editor, { html: '
01\n23\n45\n
' }); + editor.formatText(5, 1, { header: 1 }); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01') + .insert('\n', { 'code-block': true }) + .insert('23') + .insert('\n', { header: 1 }) + .insert('45') + .insert('\n', { 'code-block': true }), + ); + expect(editor.scroll.domNode.innerHTML).toEqual( + '
01\n

23

45\n
', ); - expect(editor.scroll.domNode.innerHTML).toEqual('
01\n

23

45\n
'); }); it('format imprecise bounds', function() { - let editor = this.initialize(Editor, { html: '
01\n23\n45\n
' }); - editor.formatText(1, 6, { 'header': 1 }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01').insert('\n', { 'header': 1 }) - .insert('23').insert('\n', { 'header': 1 }) - .insert('45').insert('\n', { 'code-block': true }) + const editor = this.initialize(Editor, { html: '
01\n23\n45\n
' }); + editor.formatText(1, 6, { header: 1 }); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01') + .insert('\n', { header: 1 }) + .insert('23') + .insert('\n', { header: 1 }) + .insert('45') + .insert('\n', { 'code-block': true }), + ); + expect(editor.scroll.domNode.innerHTML).toEqual( + '

01

23

45\n
', ); - expect(editor.scroll.domNode.innerHTML).toEqual('

01

23

45\n
'); }); it('format without newline', function() { - let editor = this.initialize(Editor, { html: '
01\n23\n45\n
' }); - editor.formatText(3, 1, { 'header': 1 }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01').insert('\n', { 'code-block': true }) - .insert('23').insert('\n', { 'code-block': true }) - .insert('45').insert('\n', { 'code-block': true }) + const editor = this.initialize(Editor, { html: '
01\n23\n45\n
' }); + editor.formatText(3, 1, { header: 1 }); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01') + .insert('\n', { 'code-block': true }) + .insert('23') + .insert('\n', { 'code-block': true }) + .insert('45') + .insert('\n', { 'code-block': true }), ); expect(editor.scroll.domNode.innerHTML).toEqual('
01\n23\n45\n
'); }); it('format line', function() { - let editor = this.initialize(Editor, { html: '
01\n23\n45\n
' }); - editor.formatLine(3, 1, { 'header': 1 }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('01').insert('\n', { 'code-block': true }) - .insert('23').insert('\n', { 'header': 1 }) - .insert('45').insert('\n', { 'code-block': true }) + const editor = this.initialize(Editor, { html: '
01\n23\n45\n
' }); + editor.formatLine(3, 1, { header: 1 }); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('01') + .insert('\n', { 'code-block': true }) + .insert('23') + .insert('\n', { header: 1 }) + .insert('45') + .insert('\n', { 'code-block': true }), + ); + expect(editor.scroll.domNode.innerHTML).toEqual( + '
01\n

23

45\n
', ); - expect(editor.scroll.domNode.innerHTML).toEqual('
01\n

23

45\n
'); }); it('ignore formatAt', function() { - let editor = this.initialize(Editor, '
0123
'); + const editor = this.initialize(Editor, '
0123
'); editor.formatText(1, 1, { bold: true }); - expect(editor.getDelta()).toEqual(new Delta().insert('0123').insert('\n', { 'code-block': true })); + expect(editor.getDelta()).toEqual( + new Delta().insert('0123').insert('\n', { 'code-block': true }), + ); expect(editor.scroll.domNode).toEqualHTML('
0123
'); }); it('partial block modification applyDelta', function() { - let editor = this.initialize(Editor, { html: '
a\nb\n\n
' }); - let delta = new Delta() + const editor = this.initialize(Editor, { html: '
a\nb\n\n
' }); + const delta = new Delta() .retain(3) .insert('\n', { 'code-block': true }) .delete(1) .retain(1, { 'code-block': null }); editor.applyDelta(delta); - expect(editor.scroll.domNode.innerHTML).toEqual('
a\nb\n


'); + expect(editor.scroll.domNode.innerHTML).toEqual( + '
a\nb\n


', + ); }); }); diff --git a/test/unit/formats/color.js b/test/unit/formats/color.js index 482f438cc0..9d50cd41fe 100644 --- a/test/unit/formats/color.js +++ b/test/unit/formats/color.js @@ -1,37 +1,48 @@ import Delta from 'quill-delta'; import Editor from '../../../core/editor'; - describe('Color', function() { it('add', function() { - let editor = this.initialize(Editor, '

0123

'); - editor.formatText(1, 2, { color: 'red' }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0') - .insert('12', { color: 'red' }) - .insert('3\n') + const editor = this.initialize(Editor, '

0123

'); + editor.formatText(1, 2, { color: 'red' }); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0') + .insert('12', { color: 'red' }) + .insert('3\n'), + ); + expect(editor.scroll.domNode).toEqualHTML( + '

0123

', ); - expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('remove', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize( + Editor, + '

0123

', + ); editor.formatText(1, 2, { color: false }); - let delta = new Delta().insert('0').insert('12', { bold: true }).insert('3\n'); + const delta = new Delta() + .insert('0') + .insert('12', { bold: true }) + .insert('3\n'); expect(editor.getDelta()).toEqual(delta); expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('remove unwrap', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize( + Editor, + '

0123

', + ); editor.formatText(1, 2, { color: false }); expect(editor.getDelta()).toEqual(new Delta().insert('0123\n')); expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('invalid scope', function() { - let editor = this.initialize(Editor, '

0123

'); - let initial = editor.scroll.domNode.innerHTML; + const editor = this.initialize(Editor, '

0123

'); + const initial = editor.scroll.domNode.innerHTML; editor.formatText(4, 1, { color: 'red' }); expect(editor.getDelta()).toEqual(new Delta().insert('0123\n')); expect(editor.scroll.domNode).toEqualHTML(initial); diff --git a/test/unit/formats/header.js b/test/unit/formats/header.js index eccb9c8b84..43dbc8917e 100644 --- a/test/unit/formats/header.js +++ b/test/unit/formats/header.js @@ -1,34 +1,30 @@ import Delta from 'quill-delta'; import Editor from '../../../core/editor'; - describe('Header', function() { it('add', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.formatText(4, 1, { header: 1 }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0123', { italic: true }) - .insert('\n', { header: 1 }) + expect(editor.getDelta()).toEqual( + new Delta().insert('0123', { italic: true }).insert('\n', { header: 1 }), ); expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('remove', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.formatText(4, 1, { header: false }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0123', { italic: true }) - .insert('\n') + expect(editor.getDelta()).toEqual( + new Delta().insert('0123', { italic: true }).insert('\n'), ); expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('change', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.formatText(4, 1, { header: 2 }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0123', { italic: true }) - .insert('\n', { header: 2 }) + expect(editor.getDelta()).toEqual( + new Delta().insert('0123', { italic: true }).insert('\n', { header: 2 }), ); expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); diff --git a/test/unit/formats/indent.js b/test/unit/formats/indent.js index 8d1992b162..b6376a9077 100644 --- a/test/unit/formats/indent.js +++ b/test/unit/formats/indent.js @@ -1,19 +1,27 @@ import Delta from 'quill-delta'; import Editor from '../../../core/editor'; - describe('Indent', function() { it('+1', function() { - let editor = this.initialize(Editor, ''); - editor.formatText(4, 1, { 'indent': '+1' }); - expect(editor.getDelta()).toEqual(new Delta().insert('0123').insert('\n', { list: 'bullet', indent: 1 })); - expect(editor.scroll.domNode).toEqualHTML(''); + const editor = this.initialize(Editor, ''); + editor.formatText(4, 1, { indent: '+1' }); + expect(editor.getDelta()).toEqual( + new Delta().insert('0123').insert('\n', { list: 'bullet', indent: 1 }), + ); + expect(editor.scroll.domNode).toEqualHTML( + '', + ); }); it('-1', function() { - let editor = this.initialize(Editor, ''); - editor.formatText(4, 1, { 'indent': '-1' }); - expect(editor.getDelta()).toEqual(new Delta().insert('0123').insert('\n', { list: 'bullet' })); + const editor = this.initialize( + Editor, + '', + ); + editor.formatText(4, 1, { indent: '-1' }); + expect(editor.getDelta()).toEqual( + new Delta().insert('0123').insert('\n', { list: 'bullet' }), + ); expect(editor.scroll.domNode).toEqualHTML(''); }); }); diff --git a/test/unit/formats/link.js b/test/unit/formats/link.js index 0762d6594e..8d09d33f81 100644 --- a/test/unit/formats/link.js +++ b/test/unit/formats/link.js @@ -2,56 +2,76 @@ import Delta from 'quill-delta'; import Editor from '../../../core/editor'; import Link from '../../../formats/link'; - describe('Link', function() { it('add', function() { - let editor = this.initialize(Editor, '

0123

'); - editor.formatText(1, 2, { link: 'https://quilljs.com' }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0') - .insert('12', { link: 'https://quilljs.com' }) - .insert('3\n') + const editor = this.initialize(Editor, '

0123

'); + editor.formatText(1, 2, { link: 'https://quilljs.com' }); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0') + .insert('12', { link: 'https://quilljs.com' }) + .insert('3\n'), + ); + expect(editor.scroll.domNode).toEqualHTML( + '

0123

', ); - expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('add invalid', function() { - let editor = this.initialize(Editor, '

0123

'); - editor.formatText(1, 2, { link: 'javascript:alert(0);' }); // eslint-disable-line no-script-url - expect(editor.getDelta()).toEqual(new Delta() - .insert('0') - .insert('12', { link: Link.SANITIZED_URL }) - .insert('3\n') + const editor = this.initialize(Editor, '

0123

'); + editor.formatText(1, 2, { link: 'javascript:alert(0);' }); // eslint-disable-line no-script-url + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0') + .insert('12', { link: Link.SANITIZED_URL }) + .insert('3\n'), ); }); it('add non-whitelisted protocol', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.formatText(1, 2, { link: 'gopher://quilljs.com' }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0') - .insert('12', { link: Link.SANITIZED_URL }) - .insert('3\n') + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0') + .insert('12', { link: Link.SANITIZED_URL }) + .insert('3\n'), + ); + expect(editor.scroll.domNode).toEqualHTML( + '

0123

', ); - expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('change', function() { - let editor = this.initialize(Editor, '

0123

'); - editor.formatText(1, 2, { link: 'https://quilljs.com' }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0') - .insert('12', { link: 'https://quilljs.com' }) - .insert('3\n') + const editor = this.initialize( + Editor, + '

0123

', + ); + editor.formatText(1, 2, { link: 'https://quilljs.com' }); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0') + .insert('12', { link: 'https://quilljs.com' }) + .insert('3\n'), + ); + expect(editor.scroll.domNode).toEqualHTML( + '

0123

', ); - expect(editor.scroll.domNode).toEqualHTML('

0123

'); }); it('remove', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize( + Editor, + '

0123

', + ); editor.formatText(1, 2, { link: false }); - let delta = new Delta().insert('0').insert('12', { size: 'large' }).insert('3\n'); + const delta = new Delta() + .insert('0') + .insert('12', { size: 'large' }) + .insert('3\n'); expect(editor.getDelta()).toEqual(delta); - expect(editor.scroll.domNode).toEqualHTML('

0123

'); + expect(editor.scroll.domNode).toEqualHTML( + '

0123

', + ); }); }); diff --git a/test/unit/formats/list.js b/test/unit/formats/list.js index d5447b5666..cc17e2824d 100644 --- a/test/unit/formats/list.js +++ b/test/unit/formats/list.js @@ -1,19 +1,21 @@ import Delta from 'quill-delta'; import Editor from '../../../core/editor'; - describe('List', function() { it('add', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `

0123

5678

-

0123

` +

0123

`, ); editor.formatText(9, 1, { list: 'ordered' }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0123\n5678') - .insert('\n', { list: 'ordered' }) - .insert('0123\n') + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0123\n5678') + .insert('\n', { list: 'ordered' }) + .insert('0123\n'), ); expect(this.container).toEqualHTML(`

0123

@@ -23,20 +25,24 @@ describe('List', function() { }); it('checklist', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `

0123

5678

0123

- `); + `, + ); editor.scroll.domNode.classList.add('ql-editor'); editor.formatText(4, 1, { list: 'checked' }); editor.formatText(9, 1, { list: 'unchecked' }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0123') - .insert('\n', { list: 'checked' }) - .insert('5678') - .insert('\n', { list: 'unchecked' }) - .insert('0123\n') + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0123') + .insert('\n', { list: 'checked' }) + .insert('5678') + .insert('\n', { list: 'unchecked' }) + .insert('0123\n'), ); expect(this.container).toEqualHTML(` `); }); it('replace split', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `
  1. 0123
  2. 5678
  3. 0123
  4. -
` + `, ); - editor.formatText(9, 1, { list: 'bullet' }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0123') - .insert('\n', { list: 'ordered' }) - .insert('5678') - .insert('\n', { list: 'bullet' }) - .insert('0123') - .insert('\n', { list: 'ordered' }) + editor.formatText(9, 1, { list: 'bullet' }); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0123') + .insert('\n', { list: 'ordered' }) + .insert('5678') + .insert('\n', { list: 'bullet' }) + .insert('0123') + .insert('\n', { list: 'ordered' }), ); expect(this.container).toEqualHTML(`
  1. 0123
-
  1. 0123
` - ); +
  1. 0123
`); }); it('split checklist', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + ` ` + `, ); - editor.formatText(9, 1, { list: 'unchecked' }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0123') - .insert('\n', { list: 'bullet' }) - .insert('5678') - .insert('\n', { list: 'unchecked' }) - .insert('0123') - .insert('\n', { list: 'bullet' }) + editor.formatText(9, 1, { list: 'unchecked' }); + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0123') + .insert('\n', { list: 'bullet' }) + .insert('5678') + .insert('\n', { list: 'unchecked' }) + .insert('0123') + .insert('\n', { list: 'bullet' }), ); expect(this.container).toEqualHTML(` - ` - ); + `); }); it('empty line interop', function() { - let editor = this.initialize(Editor, '

'); + const editor = this.initialize(Editor, '

'); editor.insertText(0, 'Test'); expect(this.container).toEqualHTML('
  1. Test
'); editor.deleteText(0, 4); @@ -258,45 +294,51 @@ describe('List', function() { }); it('delete multiple items', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `
  1. 0123
  2. 5678
  3. 0123
  4. -
` + `, ); editor.deleteText(2, 5); expect(this.container).toEqualHTML(`
  1. 0178
  2. 0123
  3. -
` - ); + `); }); it('delete across last item', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `
  1. 0123
-

5678

` +

5678

`, ); editor.deleteText(2, 5); expect(this.container).toEqualHTML('

0178

'); }); it('delete partial', function() { - let editor = this.initialize(Editor, '

0123

'); + const editor = this.initialize(Editor, '

0123

'); editor.deleteText(2, 5); expect(this.container).toEqualHTML(''); }); it('nested list replacement', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `
  1. One
  2. Alpha
  3. Two
- `); + `, + ); editor.formatLine(1, 10, { list: 'bullet' }); expect(this.container).toEqualHTML(`