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(`
The Whale
', - 'The Whale
', '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([ - '' + 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( + [ + '${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('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, '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, 'HelloWorld
HelloWorld
HelloWorld
'); }); it('join empty lines', 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('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
01234
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
-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
01
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('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('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
'); + 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, '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, + `013
@@ -252,12 +283,15 @@ describe('Editor', function() { }); it('end of document', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `0123
@@ -268,177 +302,254 @@ describe('Editor', function() { describe('applyDelta', function() { it('insert', function() { - let editor = this.initialize(Editor, ''); + const editor = this.initialize(Editor, ''); editor.applyDelta(new Delta().insert('01')); expect(this.container).toEqualHTML('01
'); }); it('attributed insert', function() { - let editor = this.initialize(Editor, '0123
'); + const editor = this.initialize(Editor, '0123
'); editor.applyDelta(new Delta().retain(2).insert('|', { bold: true })); expect(this.container).toEqualHTML('01|23
'); }); it('format', function() { - let editor = this.initialize(Editor, '01
'); + const editor = this.initialize(Editor, '01
'); editor.applyDelta(new Delta().retain(2, { bold: true })); expect(this.container).toEqualHTML('01
'); }); it('discontinuous formats', function() { - let editor = this.initialize(Editor, ''); - let delta = new Delta() + const editor = this.initialize(Editor, ''); + const delta = new Delta() .insert('ab', { bold: true }) .insert('23\n45') .insert('cd', { bold: true }); editor.applyDelta(delta); - expect(this.container).toEqualHTML('ab23
45cd
'); + expect(this.container).toEqualHTML( + 'ab23
45cd
', + ); }); it('unformatted insert', function() { - let editor = this.initialize(Editor, '01
'); + const editor = this.initialize(Editor, '01
'); editor.applyDelta(new Delta().retain(1).insert('|')); expect(this.container).toEqualHTML('0|1
'); }); it('insert at format boundary', function() { - let editor = this.initialize(Editor, '01
'); + const editor = this.initialize(Editor, '01
'); editor.applyDelta(new Delta().retain(1).insert('|', { strike: true })); expect(this.container).toEqualHTML('0|1
01
'); + const editor = this.initialize(Editor, ''); + editor.applyDelta( + new Delta().insert({ image: '/assets/favicon.png' }, { italic: true }), + ); + expect(this.container).toEqualHTML( + '
', + ); }); it('old embed', function() { - let editor = this.initialize(Editor, ''); - editor.applyDelta(new Delta().insert(1, { image: '/assets/favicon.png', italic: true })); - expect(this.container).toEqualHTML('
'); + const editor = this.initialize(Editor, ''); + editor.applyDelta( + new Delta().insert(1, { image: '/assets/favicon.png', italic: true }), + ); + expect(this.container).toEqualHTML( + '
', + ); }); it('old list', function() { - let editor = this.initialize(Editor, ''); - editor.applyDelta(new Delta().insert('\n', { bullet: true }).insert('\n', { list: true })); - expect(this.container).toEqualHTML('
0123
'); + const editor = this.initialize(Editor, '0123
'); editor.applyDelta(new Delta().retain(2).insert({ video: '#' })); - expect(this.container).toEqualHTML('01
23
'); + expect(this.container).toEqualHTML( + '01
23
', + ); }); it('append formatted block embed', function() { - let editor = this.initialize(Editor, '0123
0123
0123
0123
0123
'); + const editor = this.initialize(Editor, '0123
'); editor.applyDelta(new Delta().retain(5).insert('5678')); expect(this.container).toEqualHTML('0123
5678
'); }); it('append newline', function() { - let editor = this.initialize(Editor, '0123
'); + const editor = this.initialize(Editor, '0123
'); editor.applyDelta(new Delta().retain(5).insert('\n', { header: 2 })); expect(this.container).toEqualHTML('0123
0123
'); - editor.applyDelta(new Delta().retain(5).insert('5678').insert('\n', { header: 2 })); + const editor = this.initialize(Editor, '0123
'); + editor.applyDelta( + new Delta() + .retain(5) + .insert('5678') + .insert('\n', { header: 2 }), + ); expect(this.container).toEqualHTML('0123
0123
'); + const editor = this.initialize(Editor, '0123
'); editor.applyDelta(new Delta().retain(5).insert('5678\n', { header: 2 })); expect(this.container).toEqualHTML('0123
0123
'); - editor.applyDelta(new Delta().retain(2).insert('ab\n', { header: 1 }).retain(3).insert('cd\n', { header: 2 })); + const editor = this.initialize(Editor, '0123
'); + editor.applyDelta( + new Delta() + .retain(2) + .insert('ab\n', { header: 1 }) + .retain(3) + .insert('cd\n', { header: 2 }), + ); expect(this.container).toEqualHTML('23
0123
'); - editor.applyDelta(new Delta().retain(5).insert('5678').insert({ image: '/assets/favicon.png' }).insert('\n', { header: 2 })); - expect(this.container).toEqualHTML('0123
0123
'); + editor.applyDelta( + new Delta() + .retain(5) + .insert('5678') + .insert({ image: '/assets/favicon.png' }) + .insert('\n', { header: 2 }), + ); + expect(this.container).toEqualHTML( + '0123
0123
'); - editor.applyDelta(new Delta().retain(5) - .insert('56').insert('\n', { header: 1 }) - .insert('89').insert('\n', { header: 2 }) + const editor = this.initialize(Editor, '0123
'); + editor.applyDelta( + new Delta() + .retain(5) + .insert('56') + .insert('\n', { header: 1 }) + .insert('89') + .insert('\n', { header: 2 }), ); expect(this.container).toEqualHTML('0123
0
1\n23\n
0
1\n23\n
2
'); }); }); describe('getFormat()', function() { it('unformatted', function() { - let editor = this.initialize(Editor, '0123
'); + const editor = this.initialize(Editor, '0123
'); expect(editor.getFormat(1)).toEqual({}); - }) + }); it('formatted', function() { - let editor = this.initialize(Editor, '0123
'); }); it('newlines', function() { - let quill = this.initialize(Quill, 'Test
'); + const quill = this.initialize( + Quill, + 'Test
', + ); expect(quill.getContents()).toEqual( - new Delta().insert('Test').insert('\n', { align: 'center' }) + new Delta().insert('Test').insert('\n', { align: 'center' }), ); expect(quill.root).toEqualHTML('Test
'); }); @@ -51,46 +53,88 @@ describe('Quill', function() { it('deleteText()', function() { this.quill.deleteText(3, 2); - let change = new Delta().retain(3).delete(2); + const change = new Delta().retain(3).delete(2); expect(this.quill.root).toEqualHTML('012567
'); - expect(this.quill.emitter.emit).toHaveBeenCalledWith(Emitter.events.TEXT_CHANGE, change, this.oldDelta, Emitter.sources.API); + expect(this.quill.emitter.emit).toHaveBeenCalledWith( + Emitter.events.TEXT_CHANGE, + change, + this.oldDelta, + Emitter.sources.API, + ); }); it('format()', function() { this.quill.setSelection(3, 2); this.quill.format('bold', true); - let change = new Delta().retain(3).retain(2, { bold: true }); - expect(this.quill.root).toEqualHTML('01234567
'); - expect(this.quill.emitter.emit).toHaveBeenCalledWith(Emitter.events.TEXT_CHANGE, change, this.oldDelta, Emitter.sources.API); + const change = new Delta().retain(3).retain(2, { bold: true }); + expect(this.quill.root).toEqualHTML( + '01234567
', + ); + expect(this.quill.emitter.emit).toHaveBeenCalledWith( + Emitter.events.TEXT_CHANGE, + change, + this.oldDelta, + Emitter.sources.API, + ); expect(this.quill.getSelection()).toEqual(new Range(3, 2)); }); it('formatLine()', function() { this.quill.formatLine(1, 1, 'header', 2); - let change = new Delta().retain(8).retain(1, { header: 2}); + const change = new Delta().retain(8).retain(1, { header: 2 }); expect(this.quill.root).toEqualHTML('01234567
'); - expect(this.quill.emitter.emit).toHaveBeenCalledWith(Emitter.events.TEXT_CHANGE, change, this.oldDelta, Emitter.sources.API); + const change = new Delta().retain(3).retain(2, { bold: true }); + expect(this.quill.root).toEqualHTML( + '01234567
', + ); + expect(this.quill.emitter.emit).toHaveBeenCalledWith( + Emitter.events.TEXT_CHANGE, + change, + this.oldDelta, + Emitter.sources.API, + ); }); it('insertEmbed()', function() { this.quill.insertEmbed(5, 'image', '/assets/favicon.png'); - let change = new Delta().retain(5).insert({ image: '/assets/favicon.png'}, { italic: true }); - expect(this.quill.root).toEqualHTML('01234567
'); - expect(this.quill.emitter.emit).toHaveBeenCalledWith(Emitter.events.TEXT_CHANGE, change, this.oldDelta, Emitter.sources.API); + const change = new Delta() + .retain(5) + .insert({ image: '/assets/favicon.png' }, { italic: true }); + expect(this.quill.root).toEqualHTML( + '01234567
', + ); + expect(this.quill.emitter.emit).toHaveBeenCalledWith( + Emitter.events.TEXT_CHANGE, + change, + this.oldDelta, + Emitter.sources.API, + ); }); it('insertText()', function() { this.quill.insertText(5, '|', 'bold', true); - let change = new Delta().retain(5).insert('|', { bold: true, italic: true }); - expect(this.quill.root).toEqualHTML('01234|567
'); - expect(this.quill.emitter.emit).toHaveBeenCalledWith(Emitter.events.TEXT_CHANGE, change, this.oldDelta, Emitter.sources.API); + const change = new Delta() + .retain(5) + .insert('|', { bold: true, italic: true }); + expect(this.quill.root).toEqualHTML( + '01234|567
', + ); + expect(this.quill.emitter.emit).toHaveBeenCalledWith( + Emitter.events.TEXT_CHANGE, + change, + this.oldDelta, + Emitter.sources.API, + ); }); it('enable/disable', function() { @@ -109,36 +153,51 @@ describe('Quill', function() { }); it('getFormat()', function() { - let formats = this.quill.getFormat(5); + const formats = this.quill.getFormat(5); expect(formats).toEqual({ italic: true }); }); it('getSelection()', function() { expect(this.quill.getSelection()).toEqual(null); - let range = new Range(1, 2); + const range = new Range(1, 2); this.quill.setSelection(range); expect(this.quill.getSelection()).toEqual(range); }); it('removeFormat()', function() { this.quill.removeFormat(5, 1); - let change = new Delta().retain(5).retain(1, { italic: null }); + const change = new Delta().retain(5).retain(1, { italic: null }); expect(this.quill.root).toEqualHTML('01234567
'); - expect(this.quill.emitter.emit).toHaveBeenCalledWith(Emitter.events.TEXT_CHANGE, change, this.oldDelta, Emitter.sources.API); + expect(this.quill.emitter.emit).toHaveBeenCalledWith( + Emitter.events.TEXT_CHANGE, + change, + this.oldDelta, + Emitter.sources.API, + ); }); it('updateContents() delta', function() { - let delta = new Delta().retain(5).insert('|'); + const delta = new Delta().retain(5).insert('|'); this.quill.updateContents(delta); expect(this.quill.root).toEqualHTML('01234|567
'); - expect(this.quill.emitter.emit).toHaveBeenCalledWith(Emitter.events.TEXT_CHANGE, delta, this.oldDelta, Emitter.sources.API); + expect(this.quill.emitter.emit).toHaveBeenCalledWith( + Emitter.events.TEXT_CHANGE, + delta, + this.oldDelta, + Emitter.sources.API, + ); }); it('updateContents() ops array', function() { - let delta = new Delta().retain(5).insert('|'); + const delta = new Delta().retain(5).insert('|'); this.quill.updateContents(delta.ops); expect(this.quill.root).toEqualHTML('01234|567
'); - expect(this.quill.emitter.emit).toHaveBeenCalledWith(Emitter.events.TEXT_CHANGE, delta, this.oldDelta, Emitter.sources.API); + expect(this.quill.emitter.emit).toHaveBeenCalledWith( + Emitter.events.TEXT_CHANGE, + delta, + this.oldDelta, + Emitter.sources.API, + ); }); }); @@ -152,17 +211,25 @@ describe('Quill', function() { it('api text insert', function() { this.quill.insertText(2, '!'); - let delta = new Delta().retain(2).insert('!'); - expect(this.quill.emitter.emit) - .toHaveBeenCalledWith(Emitter.events.TEXT_CHANGE, delta, this.oldDelta, Emitter.sources.API); + const delta = new Delta().retain(2).insert('!'); + expect(this.quill.emitter.emit).toHaveBeenCalledWith( + Emitter.events.TEXT_CHANGE, + delta, + this.oldDelta, + Emitter.sources.API, + ); }); it('user text insert', function(done) { this.container.firstChild.firstChild.firstChild.data = '01!23'; - let delta = new Delta().retain(2).insert('!'); + const delta = new Delta().retain(2).insert('!'); setTimeout(() => { - expect(this.quill.emitter.emit) - .toHaveBeenCalledWith(Emitter.events.TEXT_CHANGE, delta, this.oldDelta, Emitter.sources.USER); + expect(this.quill.emitter.emit).toHaveBeenCalledWith( + Emitter.events.TEXT_CHANGE, + delta, + this.oldDelta, + Emitter.sources.USER, + ); done(); }, 1); }); @@ -171,15 +238,20 @@ describe('Quill', function() { this.quill.setText('aaaa\n'); this.quill.setSelection(2); this.quill.update(); - let old = this.quill.getContents(); - let textNode = this.container.firstChild.firstChild.firstChild; + const old = this.quill.getContents(); + const textNode = this.container.firstChild.firstChild.firstChild; textNode.data = 'aaaaa'; this.quill.selection.setNativeRange(textNode.data, 3); // this.quill.selection.update(Emitter.sources.SILENT); - let delta = new Delta().retain(2).insert('a'); + const delta = new Delta().retain(2).insert('a'); setTimeout(() => { - let args = this.quill.emitter.emit.calls.mostRecent().args; - expect(args).toEqual([Emitter.events.TEXT_CHANGE, delta, old, Emitter.sources.USER]); + const { args } = this.quill.emitter.emit.calls.mostRecent(); + expect(args).toEqual([ + Emitter.events.TEXT_CHANGE, + delta, + old, + Emitter.sources.USER, + ]); done(); }, 1); }); @@ -187,36 +259,38 @@ describe('Quill', function() { describe('setContents()', function() { it('empty', function() { - let quill = this.initialize(Quill, ''); - let delta = new Delta().insert('\n'); + const quill = this.initialize(Quill, ''); + const delta = new Delta().insert('\n'); quill.setContents(delta); expect(quill.getContents()).toEqual(delta); expect(quill.root).toEqualHTML('Hello World!
'); }); it('multiple lines', function() { - let quill = this.initialize(Quill, ''); - let delta = new Delta().insert('Hello\n\nWorld!\n'); + const quill = this.initialize(Quill, ''); + const delta = new Delta().insert('Hello\n\nWorld!\n'); quill.setContents(delta); expect(quill.getContents()).toEqual(delta); expect(quill.root).toEqualHTML('Hello
World!
'); }); it('basic formats', function() { - let quill = this.initialize(Quill, ''); - let delta = new Delta().insert('Welcome').insert('\n', { header: 1 }) - .insert('Hello\n') - .insert('World') - .insert('!', { bold: true }) - .insert('\n'); + const quill = this.initialize(Quill, ''); + const delta = new Delta() + .insert('Welcome') + .insert('\n', { header: 1 }) + .insert('Hello\n') + .insert('World') + .insert('!', { bold: true }) + .insert('\n'); quill.setContents(delta); expect(quill.getContents()).toEqual(delta); expect(quill.root).toEqualHTML(` @@ -227,29 +301,35 @@ describe('Quill', function() { }); it('array of operations', function() { - let quill = this.initialize(Quill, ''); - let delta = new Delta().insert('test').insert('123', { bold: true }).insert('\n'); + const quill = this.initialize(Quill, ''); + const delta = new Delta() + .insert('test') + .insert('123', { bold: true }) + .insert('\n'); quill.setContents(delta.ops); expect(quill.getContents()).toEqual(delta); }); it('json', function() { - let quill = this.initialize(Quill, ''); - let delta = { ops: [{ insert: 'test\n'}] }; + const quill = this.initialize(Quill, ''); + const delta = { ops: [{ insert: 'test\n' }] }; quill.setContents(delta); expect(quill.getContents()).toEqual(new Delta(delta)); }); it('no trailing newline', function() { - let quill = this.initialize(Quill, 'Bold
Not bold
'); - let contents = quill.getContents(); - let delta = quill.setContents(contents); + const quill = this.initialize( + Quill, + 'Bold
Not bold
', + ); + const contents = quill.getContents(); + const delta = quill.setContents(contents); expect(quill.getContents()).toEqual(contents); expect(delta).toEqual(contents.delete(contents.length())); }); @@ -257,37 +337,37 @@ describe('Quill', function() { describe('setText()', function() { it('overwrite', function() { - let quill = this.initialize(Quill, 'abc
'); }); it('set to newline', function() { - let quill = this.initialize(Quill, 'abc
'); }); it('return carriage', function() { - let quill = this.initialize(Quill, 'Test
'); + const quill = this.initialize(Quill, 'Test
'); quill.setText('\r'); expect(quill.root).toEqualHTML('Test
'); + const quill = this.initialize(Quill, 'Test
'); quill.setText('\r\n'); expect(quill.root).toEqualHTML('0123
', this.container.lastChild); + this.selection = this.initialize( + Selection, + '0123
', + this.container.lastChild, + ); this.textarea = this.container.querySelector('textarea'); this.textarea.focus(); this.textarea.select(); @@ -26,7 +29,7 @@ describe('Selection', function() { }); it('restore last range', function() { - let range = new Range(1, 2); + const range = new Range(1, 2); this.selection.setRange(range); this.textarea.focus(); this.textarea.select(); @@ -39,63 +42,77 @@ describe('Selection', function() { describe('getRange()', function() { it('empty document', function() { - let selection = this.initialize(Selection, ''); + const selection = this.initialize(Selection, ''); selection.setNativeRange(this.container.querySelector('br'), 0); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range.index).toEqual(0); expect(range.length).toEqual(0); }); it('empty line', function() { - let selection = this.initialize(Selection, '0
3
'); + const selection = this.initialize( + Selection, + '0
3
', + ); selection.setNativeRange(this.container.querySelector('br'), 0); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range.index).toEqual(2); expect(range.length).toEqual(0); }); it('end of line', function() { - let selection = this.initialize(Selection, '0
'); + const selection = this.initialize(Selection, '0
'); selection.setNativeRange(this.container.firstChild.firstChild, 1); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range.index).toEqual(1); expect(range.length).toEqual(0); }); it('text node', function() { - let selection = this.initialize(Selection, '0123
'); + const selection = this.initialize(Selection, '0123
'); selection.setNativeRange(this.container.firstChild.firstChild, 1); - let [range, ] = selection.getRange(); + const [range] = selection.getRange(); expect(range.index).toEqual(1); expect(range.length).toEqual(0); }); it('line boundaries', function() { - let selection = this.initialize(Selection, '12
'); - selection.setNativeRange(this.container.firstChild, 0, this.container.lastChild.lastChild, 2); - let [range, ] = selection.getRange(); + const selection = this.initialize(Selection, '12
'); + selection.setNativeRange( + this.container.firstChild, + 0, + this.container.lastChild.lastChild, + 2, + ); + const [range] = selection.getRange(); expect(range.index).toEqual(0); expect(range.length).toEqual(3); }); it('nested text node', function() { - let selection = this.initialize(Selection, ` + const selection = this.initialize( + Selection, + `01
@@ -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
012345
01
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, + `01
012345
012345
@@ -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, '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, '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
\n
\n'); + expect(editor.scroll.domNode.innerHTML).toEqual( + '
\n', + ); }); it('merge', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `
0
0
2\n\n
2\n\n- `); + `, + ); editor.scroll.lines().forEach(function(line) { line.optimize(); - }) + }); expect(editor.scroll.domNode).toEqualHTML(`
0\n0\n
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: '4567\n' }); + const editor = this.initialize(Editor, { + html: '
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
0123\n
01\n34\n67\n
01\n34\n67\n
01\n34\n
01\n34\n
01\n34\n
01\n34\n
01\n
01\n
56\n89\n' }); + const editor = this.initialize(Editor, { + html: '
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('
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\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
45\n', ); - expect(editor.scroll.domNode.innerHTML).toEqual('
01\n
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( + '
45\n', ); - expect(editor.scroll.domNode.innerHTML).toEqual('
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
45\n', ); - expect(editor.scroll.domNode.innerHTML).toEqual('
01\n
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
a\nb\n
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('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('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(`0123
0123
- `); + `, + ); editor.formatText(9, 1, { list: null }); expect(editor.getDelta()).toEqual(new Delta().insert('0123\n5678\n0123\n')); expect(this.container).toEqualHTML(` @@ -65,16 +74,20 @@ describe('List', function() { }); it('replace', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `0123
0123
- `); + `, + ); editor.formatText(9, 1, { list: 'bullet' }); - expect(editor.getDelta()).toEqual(new Delta() - .insert('0123\n5678') - .insert('\n', { list: 'bullet' }) - .insert('0123\n') + expect(editor.getDelta()).toEqual( + new Delta() + .insert('0123\n5678') + .insert('\n', { list: 'bullet' }) + .insert('0123\n'), ); expect(this.container).toEqualHTML(`0123
@@ -84,15 +97,17 @@ describe('List', function() { }); it('replace checklist with bullet', function() { - let editor = this.initialize(Editor, ` + const editor = this.initialize( + Editor, + `5678
5678
-5678
5678
` +5678
`, ); editor.deleteText(2, 5); expect(this.container).toEqualHTML('0178
'); }); it('delete partial', function() { - let editor = this.initialize(Editor, '0123
0123
Test
'); + const editor = this.initialize( + Editor, + 'Test
', + ); editor.formatLine(4, 1, { list: 'bullet' }); - expect(this.container).toEqualHTML('a2 + b2 = c2
'); + const editor = this.initialize( + Editor, + 'a2 + b2 = c2
', + ); editor.formatText(6, 1, { script: 'super' }); - expect(editor.scroll.domNode).toEqualHTML('a2 + b2 = c2
'); + expect(editor.scroll.domNode).toEqualHTML( + 'a2 + b2 = c2
', + ); }); it('remove', function() { - let editor = this.initialize(Editor, 'a2 + b2
'); + const editor = this.initialize( + Editor, + 'a2 + b2
', + ); editor.formatText(1, 1, { script: false }); expect(editor.scroll.domNode).toEqualHTML('a2 + b2
'); }); it('replace', function() { - let editor = this.initialize(Editor, 'a2 + b2
'); + const editor = this.initialize( + Editor, + 'a2 + b2
', + ); editor.formatText(1, 1, { script: 'sub' }); - expect(editor.scroll.domNode).toEqualHTML('a2 + b2
'); + expect(editor.scroll.domNode).toEqualHTML( + 'a2 + b2
', + ); }); }); diff --git a/test/unit/modules/clipboard.js b/test/unit/modules/clipboard.js index 6f443f3917..f3de5a5568 100644 --- a/test/unit/modules/clipboard.js +++ b/test/unit/modules/clipboard.js @@ -2,7 +2,6 @@ import Delta from 'quill-delta'; import { Range } from '../../../core/selection'; import Quill from '../../../core'; - describe('Clipboard', function() { describe('events', function() { beforeEach(function() { @@ -14,15 +13,17 @@ describe('Clipboard', function() { this.quill.clipboard.container.innerHTML = '|'; this.quill.clipboard.onCapturePaste({}); setTimeout(() => { - expect(this.quill.root).toEqualHTML('01|78
'); + expect(this.quill.root).toEqualHTML( + '01|78
', + ); expect(this.quill.getSelection()).toEqual(new Range(3)); done(); }, 2); }); it('selection-change', function(done) { - let handler = { - change: function() {} + const handler = { + change() {}, }; spyOn(handler, 'change'); this.quill.on('selection-change', handler.change); @@ -36,129 +37,180 @@ describe('Clipboard', function() { it('dangerouslyPasteHTML(html)', function() { this.quill.clipboard.dangerouslyPasteHTML('abcd'); - expect(this.quill.root).toEqualHTML('abcd
'); + expect(this.quill.root).toEqualHTML( + 'abcd
', + ); }); it('dangerouslyPasteHTML(index, html)', function() { this.quill.clipboard.dangerouslyPasteHTML(2, 'ab'); - expect(this.quill.root).toEqualHTML('5678
'); + expect(this.quill.root).toEqualHTML( + '5678
', + ); }); }); describe('convert', function() { beforeEach(function() { - let quill = this.initialize(Quill, ''); + const quill = this.initialize(Quill, ''); this.clipboard = quill.clipboard; }); it('plain text', function() { - let delta = this.clipboard.convert('simple plain text'); + const delta = this.clipboard.convert('simple plain text'); expect(delta).toEqual(new Delta().insert('simple plain text')); }); it('whitespace', function() { - let html = + const html = '0 1 2
'; - let delta = this.clipboard.convert(html); - expect(delta).toEqual(new Delta().insert('0 ').insert('1', { bold: true }).insert(' 2')); + const html = '0 1 2
'; + const delta = this.clipboard.convert(html); + expect(delta).toEqual( + new Delta() + .insert('0 ') + .insert('1', { bold: true }) + .insert(' 2'), + ); }); it('intentional whitespace', function() { - let html = '0 1 2'; - let delta = this.clipboard.convert(html); - expect(delta).toEqual(new Delta().insert('0\u00a0').insert('1', { bold: true }).insert('\u00a02')); + const html = '0 1 2'; + const delta = this.clipboard.convert(html); + expect(delta).toEqual( + new Delta() + .insert('0\u00a0') + .insert('1', { bold: true }) + .insert('\u00a02'), + ); }); it('consecutive intentional whitespace', function() { - let html = ' 1 '; - let delta = this.clipboard.convert(html); - expect(delta).toEqual(new Delta().insert('\u00a0\u00a01\u00a0\u00a0', { bold: true })); + const html = ' 1 '; + const delta = this.clipboard.convert(html); + expect(delta).toEqual( + new Delta().insert('\u00a0\u00a01\u00a0\u00a0', { bold: true }), + ); }); it('break', function() { - let html = '01
34
'); - expect(delta).toEqual(new Delta().insert('01\n').insert({ video: '#' }).insert('34')); + const delta = this.clipboard.convert( + '01
34
', + ); + expect(delta).toEqual( + new Delta() + .insert('01\n') + .insert({ video: '#' }) + .insert('34'), + ); }); it('attributor and style match', function() { - let delta = this.clipboard.convert('Test
'); + const delta = this.clipboard.convert( + 'Test
', + ); expect(delta).toEqual(new Delta().insert('Test\n', { direction: 'rtl' })); }); it('nested styles', function() { - let delta = this.clipboard.convert('Test'); + const delta = this.clipboard.convert( + 'Test', + ); expect(delta).toEqual(new Delta().insert('Test', { color: 'blue' })); - }) + }); it('custom matcher', function() { this.clipboard.addMatcher(Node.TEXT_NODE, function(node, delta) { let index = 0; - let regex = /https?:\/\/[^\s]+/g; + const regex = /https?:\/\/[^\s]+/g; let match = null; - let composer = new Delta(); - while ((match = regex.exec(node.data)) !== null) { + const composer = new Delta(); + // eslint-disable-next-line no-cond-assign + while ((match = regex.exec(node.data))) { composer.retain(match.index - index); index = regex.lastIndex; composer.retain(match[0].length, { link: match[0] }); } return delta.compose(composer); }); - let delta = this.clipboard.convert('http://github.com https://quilljs.com'); - let expected = new Delta().insert('http://github.com', { link: 'http://github.com' }) - .insert(' ') - .insert('https://quilljs.com', { link: 'https://quilljs.com' }); + const delta = this.clipboard.convert( + 'http://github.com https://quilljs.com', + ); + const expected = new Delta() + .insert('http://github.com', { link: 'http://github.com' }) + .insert(' ') + .insert('https://quilljs.com', { link: 'https://quilljs.com' }); expect(delta).toEqual(expected); }); }); diff --git a/test/unit/modules/history.js b/test/unit/modules/history.js index 9f2c48b499..e554ac439a 100644 --- a/test/unit/modules/history.js +++ b/test/unit/modules/history.js @@ -2,56 +2,63 @@ import Delta from 'quill-delta'; import Quill from '../../../core'; import { getLastChangeIndex } from '../../../modules/history'; - describe('History', function() { describe('getLastChangeIndex', function() { it('delete', function() { - let delta = new Delta().retain(4).delete(2); + const delta = new Delta().retain(4).delete(2); expect(getLastChangeIndex(delta)).toEqual(4); }); it('delete with inserts', function() { - let delta = new Delta().retain(4).insert('test').delete(2); + const delta = new Delta() + .retain(4) + .insert('test') + .delete(2); expect(getLastChangeIndex(delta)).toEqual(8); }); it('insert text', function() { - let delta = new Delta().retain(4).insert('testing'); + const delta = new Delta().retain(4).insert('testing'); expect(getLastChangeIndex(delta)).toEqual(11); }); it('insert embed', function() { - let delta = new Delta().retain(4).insert({ image: true }); + const delta = new Delta().retain(4).insert({ image: true }); expect(getLastChangeIndex(delta)).toEqual(5); }); it('insert with deletes', function() { - let delta = new Delta().retain(4).delete(3).insert('!'); + const delta = new Delta() + .retain(4) + .delete(3) + .insert('!'); expect(getLastChangeIndex(delta)).toEqual(5); }); it('format', function() { - let delta = new Delta().retain(4).retain(3, { bold: true }); + const delta = new Delta().retain(4).retain(3, { bold: true }); expect(getLastChangeIndex(delta)).toEqual(7); }); it('format newline', function() { - let delta = new Delta().retain(4).retain(1, { align: 'left' }); + const delta = new Delta().retain(4).retain(1, { align: 'left' }); expect(getLastChangeIndex(delta)).toEqual(4); }); it('format mixed', function() { - let delta = new Delta().retain(4).retain(1, { align: 'left', bold: true }); + const delta = new Delta() + .retain(4) + .retain(1, { align: 'left', bold: true }); expect(getLastChangeIndex(delta)).toEqual(4); }); it('insert newline', function() { - let delta = new Delta().retain(4).insert('a\n'); + const delta = new Delta().retain(4).insert('a\n'); expect(getLastChangeIndex(delta)).toEqual(5); }); it('mutliple newline inserts', function() { - let delta = new Delta().retain(4).insert('ab\n\n'); + const delta = new Delta().retain(4).insert('ab\n\n'); expect(getLastChangeIndex(delta)).toEqual(7); }); }); @@ -61,20 +68,20 @@ describe('History', function() { this.initialize(HTMLElement, 'The lazy fox
var test = 1;\nvar bugz = 0;\n
var test = 1;\nvar bugz = 0;\n
var test = 1;\n',
- 'var bugz = 0;\n',
- '
',
- 'var test = 1;\n',
+ 'var bugz = 0;\n',
+ '
',
+ 'var test = 1;\n
',
- 'var bugz
', - ].join('')); - expect(this.quill.getContents()).toEqual(new Delta() - .insert('var test = 1;').insert('\n', { 'code-block': true }) - .insert('var bugz\n') + expect(this.quill.root.innerHTML).toEqual( + [ + 'var test = 1;\n
',
+ 'var bugz
', + ].join(''), + ); + expect(this.quill.getContents()).toEqual( + new Delta() + .insert('var test = 1;') + .insert('\n', { 'code-block': true }) + .insert('var bugz\n'), ); done(); }, HIGHLIGHT_INTERVAL + 1); diff --git a/test/unit/modules/toolbar.js b/test/unit/modules/toolbar.js index 83cb1215e0..7e04fc911d 100644 --- a/test/unit/modules/toolbar.js +++ b/test/unit/modules/toolbar.js @@ -1,7 +1,6 @@ import Quill from '../../../core/quill'; import { addControls } from '../../../modules/toolbar'; - describe('Toolbar', function() { describe('add controls', function() { it('single level', function() { @@ -15,7 +14,10 @@ describe('Toolbar', function() { }); it('nested group', function() { - addControls(this.container, [['bold', 'italic'], ['underline', 'strike']]); + addControls(this.container, [ + ['bold', 'italic'], + ['underline', 'strike'], + ]); expect(this.container).toEqualHTML(` @@ -54,10 +56,17 @@ describe('Toolbar', function() { it('everything', function() { addControls(this.container, [ - [{ font: [false, 'sans-serif', 'monospace']}, { size: ['10px', false, '18px', '32px'] }], + [ + { font: [false, 'sans-serif', 'monospace'] }, + { size: ['10px', false, '18px', '32px'] }, + ], ['bold', 'italic', 'underline', 'strike'], - [{ list: 'ordered' }, { list: 'bullet' }, { align: [false, 'center', 'right', 'justify'] }], - ['link', 'image'] + [ + { list: 'ordered' }, + { list: 'bullet' }, + { align: [false, 'center', 'right', 'justify'] }, + ], + ['link', 'image'], ]); expect(this.container).toEqualHTML(` @@ -99,27 +108,32 @@ describe('Toolbar', function() { describe('active', function() { beforeEach(function() { - let container = this.initialize(HTMLElement, ` + const container = this.initialize( + HTMLElement, + `0123
5678
5678
0123
- `); + `, + ); this.quill = new Quill(container, { modules: { toolbar: [ ['bold', 'link'], - [{ 'size': ['small', false, 'large'] }], - [{ 'align': '' }, { 'align': 'center' }] - ] + [{ size: ['small', false, 'large'] }], + [{ align: '' }, { align: 'center' }], + ], }, - theme: 'snow' + theme: 'snow', }); }); it('toggle button', function() { - let boldButton = this.container.parentNode.querySelector('button.ql-bold'); + const boldButton = this.container.parentNode.querySelector( + 'button.ql-bold', + ); this.quill.setSelection(7); expect(boldButton.classList.contains('ql-active')).toBe(true); this.quill.setSelection(2); @@ -127,7 +141,9 @@ describe('Toolbar', function() { }); it('link', function() { - let linkButton = this.container.parentNode.querySelector('button.ql-link'); + const linkButton = this.container.parentNode.querySelector( + 'button.ql-link', + ); this.quill.setSelection(12); expect(linkButton.classList.contains('ql-active')).toBe(true); this.quill.setSelection(2); @@ -135,7 +151,9 @@ describe('Toolbar', function() { }); it('dropdown', function() { - let sizeSelect = this.container.parentNode.querySelector('select.ql-size'); + const sizeSelect = this.container.parentNode.querySelector( + 'select.ql-size', + ); this.quill.setSelection(21); expect(sizeSelect.selectedIndex).toEqual(0); this.quill.setSelection(23); @@ -147,8 +165,12 @@ describe('Toolbar', function() { }); it('custom button', function() { - let centerButton = this.container.parentNode.querySelector('button.ql-align[value="center"]'); - let leftButton = this.container.parentNode.querySelector('button.ql-align[value]'); + const centerButton = this.container.parentNode.querySelector( + 'button.ql-align[value="center"]', + ); + const leftButton = this.container.parentNode.querySelector( + 'button.ql-align[value]', + ); this.quill.setSelection(17); expect(centerButton.classList.contains('ql-active')).toBe(true); expect(leftButton.classList.contains('ql-active')).toBe(false); diff --git a/test/unit/ui/picker.js b/test/unit/ui/picker.js index 04e42754c7..5d3aa8e21c 100644 --- a/test/unit/ui/picker.js +++ b/test/unit/ui/picker.js @@ -2,22 +2,32 @@ import Picker from '../../../ui/picker'; describe('Picker', function() { it('initialization', function() { - this.container.innerHTML = ''; - let picker = new Picker(this.container.firstChild); // eslint-disable-line no-unused-vars + this.container.innerHTML = + ''; + const picker = new Picker(this.container.firstChild); // eslint-disable-line no-unused-vars expect(this.container.querySelector('.ql-picker')).toBeTruthy(); expect(this.container.querySelector('.ql-active')).toBeFalsy(); - expect(this.container.querySelector('.ql-picker-item.ql-selected').outerHTML).toEqualHTML(''); - expect(this.container.querySelector('.ql-picker-item:not(.ql-selected)').outerHTML).toEqualHTML(''); + expect( + this.container.querySelector('.ql-picker-item.ql-selected').outerHTML, + ).toEqualHTML( + '', + ); + expect( + this.container.querySelector('.ql-picker-item:not(.ql-selected)') + .outerHTML, + ).toEqualHTML( + '', + ); }); it('escape charcters', function() { - let select = document.createElement('select'); - let option = document.createElement('option'); + const select = document.createElement('select'); + const option = document.createElement('option'); this.container.appendChild(select); select.appendChild(option); let value = '"Helvetica Neue", \'Helvetica\', sans-serif'; option.value = value; - value = value.replace(/\"/g, '\\"'); + value = value.replace(/"/g, '\\"'); expect(select.querySelector(`option[value="${value}"]`)).toEqual(option); }); }); diff --git a/themes/base.js b/themes/base.js index 3ed8daa71f..d6a410176b 100644 --- a/themes/base.js +++ b/themes/base.js @@ -7,37 +7,70 @@ import IconPicker from '../ui/icon-picker'; import Picker from '../ui/picker'; import Tooltip from '../ui/tooltip'; - -const ALIGNS = [ false, 'center', 'right', 'justify' ]; +const ALIGNS = [false, 'center', 'right', 'justify']; const COLORS = [ - "#000000", "#e60000", "#ff9900", "#ffff00", "#008a00", "#0066cc", "#9933ff", - "#ffffff", "#facccc", "#ffebcc", "#ffffcc", "#cce8cc", "#cce0f5", "#ebd6ff", - "#bbbbbb", "#f06666", "#ffc266", "#ffff66", "#66b966", "#66a3e0", "#c285ff", - "#888888", "#a10000", "#b26b00", "#b2b200", "#006100", "#0047b2", "#6b24b2", - "#444444", "#5c0000", "#663d00", "#666600", "#003700", "#002966", "#3d1466" + '#000000', + '#e60000', + '#ff9900', + '#ffff00', + '#008a00', + '#0066cc', + '#9933ff', + '#ffffff', + '#facccc', + '#ffebcc', + '#ffffcc', + '#cce8cc', + '#cce0f5', + '#ebd6ff', + '#bbbbbb', + '#f06666', + '#ffc266', + '#ffff66', + '#66b966', + '#66a3e0', + '#c285ff', + '#888888', + '#a10000', + '#b26b00', + '#b2b200', + '#006100', + '#0047b2', + '#6b24b2', + '#444444', + '#5c0000', + '#663d00', + '#666600', + '#003700', + '#002966', + '#3d1466', ]; -const FONTS = [ false, 'serif', 'monospace' ]; - -const HEADERS = [ '1', '2', '3', false ]; +const FONTS = [false, 'serif', 'monospace']; -const SIZES = [ 'small', false, 'large', 'huge' ]; +const HEADERS = ['1', '2', '3', false]; +const SIZES = ['small', false, 'large', 'huge']; class BaseTheme extends Theme { constructor(quill, options) { super(quill, options); - let listener = (e) => { + const listener = e => { if (!document.body.contains(quill.root)) { - return document.body.removeEventListener('click', listener); + document.body.removeEventListener('click', listener); + return; } - if (this.tooltip != null && !this.tooltip.root.contains(e.target) && - document.activeElement !== this.tooltip.textbox && !this.quill.hasFocus()) { + if ( + this.tooltip != null && + !this.tooltip.root.contains(e.target) && + document.activeElement !== this.tooltip.textbox && + !this.quill.hasFocus() + ) { this.tooltip.hide(); } if (this.pickers != null) { - this.pickers.forEach(function(picker) { + this.pickers.forEach(picker => { if (!picker.container.contains(e.target)) { picker.close(); } @@ -48,7 +81,7 @@ class BaseTheme extends Theme { } addModule(name) { - let module = super.addModule(name); + const module = super.addModule(name); if (name === 'toolbar') { this.extendToolbar(module); } @@ -56,18 +89,18 @@ class BaseTheme extends Theme { } buildButtons(buttons, icons) { - buttons.forEach((button) => { - let className = button.getAttribute('class') || ''; - className.split(/\s+/).forEach((name) => { + buttons.forEach(button => { + const className = button.getAttribute('class') || ''; + className.split(/\s+/).forEach(name => { if (!name.startsWith('ql-')) return; name = name.slice('ql-'.length); if (icons[name] == null) return; if (name === 'direction') { - button.innerHTML = icons[name][''] + icons[name]['rtl']; + button.innerHTML = icons[name][''] + icons[name].rtl; } else if (typeof icons[name] === 'string') { button.innerHTML = icons[name]; } else { - let value = button.value || ''; + const value = button.value || ''; if (value != null && icons[name][value]) { button.innerHTML = icons[name][value]; } @@ -77,33 +110,41 @@ class BaseTheme extends Theme { } buildPickers(selects, icons) { - this.pickers = selects.map((select) => { + this.pickers = selects.map(select => { if (select.classList.contains('ql-align')) { if (select.querySelector('option') == null) { fillSelect(select, ALIGNS); } return new IconPicker(select, icons.align); - } else if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) { - let format = select.classList.contains('ql-background') ? 'background' : 'color'; + } else if ( + select.classList.contains('ql-background') || + select.classList.contains('ql-color') + ) { + const format = select.classList.contains('ql-background') + ? 'background' + : 'color'; if (select.querySelector('option') == null) { - fillSelect(select, COLORS, format === 'background' ? '#ffffff' : '#000000'); + fillSelect( + select, + COLORS, + format === 'background' ? '#ffffff' : '#000000', + ); } return new ColorPicker(select, icons[format]); - } else { - if (select.querySelector('option') == null) { - if (select.classList.contains('ql-font')) { - fillSelect(select, FONTS); - } else if (select.classList.contains('ql-header')) { - fillSelect(select, HEADERS); - } else if (select.classList.contains('ql-size')) { - fillSelect(select, SIZES); - } + } + if (select.querySelector('option') == null) { + if (select.classList.contains('ql-font')) { + fillSelect(select, FONTS); + } else if (select.classList.contains('ql-header')) { + fillSelect(select, HEADERS); + } else if (select.classList.contains('ql-size')) { + fillSelect(select, SIZES); } - return new Picker(select); } + return new Picker(select); }); - let update = () => { - this.pickers.forEach(function(picker) { + const update = () => { + this.pickers.forEach(picker => { picker.update(); }); }; @@ -114,29 +155,39 @@ BaseTheme.DEFAULTS = extend(true, {}, Theme.DEFAULTS, { modules: { toolbar: { handlers: { - formula: function() { + formula: () => { this.quill.theme.tooltip.edit('formula'); }, - image: function() { - let fileInput = this.container.querySelector('input.ql-image[type=file]'); + image: () => { + let fileInput = this.container.querySelector( + 'input.ql-image[type=file]', + ); if (fileInput == null) { fileInput = document.createElement('input'); fileInput.setAttribute('type', 'file'); - fileInput.setAttribute('accept', 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'); + fileInput.setAttribute( + 'accept', + 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon', + ); fileInput.classList.add('ql-image'); fileInput.addEventListener('change', () => { if (fileInput.files != null && fileInput.files[0] != null) { - let reader = new FileReader(); - reader.onload = (e) => { - let range = this.quill.getSelection(true); - this.quill.updateContents(new Delta() - .retain(range.index) - .delete(range.length) - .insert({ image: e.target.result }) - , Emitter.sources.USER); - this.quill.setSelection(range.index + 1, Emitter.sources.SILENT); - fileInput.value = ""; - } + const reader = new FileReader(); + reader.onload = e => { + const range = this.quill.getSelection(true); + this.quill.updateContents( + new Delta() + .retain(range.index) + .delete(range.length) + .insert({ image: e.target.result }), + Emitter.sources.USER, + ); + this.quill.setSelection( + range.index + 1, + Emitter.sources.SILENT, + ); + fileInput.value = ''; + }; reader.readAsDataURL(fileInput.files[0]); } }); @@ -144,15 +195,14 @@ BaseTheme.DEFAULTS = extend(true, {}, Theme.DEFAULTS, { } fileInput.click(); }, - video: function() { + video: () => { this.quill.theme.tooltip.edit('video'); - } - } - } - } + }, + }, + }, + }, }); - class BaseTooltip extends Tooltip { constructor(quill, boundsContainer) { super(quill, boundsContainer); @@ -161,7 +211,7 @@ class BaseTooltip extends Tooltip { } listen() { - this.textbox.addEventListener('keydown', (event) => { + this.textbox.addEventListener('keydown', event => { if (event.key === 'Enter') { this.save(); event.preventDefault(); @@ -186,23 +236,31 @@ class BaseTooltip extends Tooltip { } this.position(this.quill.getBounds(this.quill.selection.savedRange)); this.textbox.select(); - this.textbox.setAttribute('placeholder', this.textbox.getAttribute(`data-${mode}`) || ''); + this.textbox.setAttribute( + 'placeholder', + this.textbox.getAttribute(`data-${mode}`) || '', + ); this.root.setAttribute('data-mode', mode); } restoreFocus() { - let scrollTop = this.quill.scrollingContainer.scrollTop; + const { scrollTop } = this.quill.scrollingContainer; this.quill.focus(); this.quill.scrollingContainer.scrollTop = scrollTop; } save() { - let value = this.textbox.value; - switch(this.root.getAttribute('data-mode')) { + let { value } = this.textbox; + switch (this.root.getAttribute('data-mode')) { case 'link': { - let scrollTop = this.quill.root.scrollTop; + const { scrollTop } = this.quill.root; if (this.linkRange) { - this.quill.formatText(this.linkRange, 'link', value, Emitter.sources.USER); + this.quill.formatText( + this.linkRange, + 'link', + value, + Emitter.sources.USER, + ); delete this.linkRange; } else { this.restoreFocus(); @@ -216,10 +274,15 @@ class BaseTooltip extends Tooltip { } // eslint-disable-next-line no-fallthrough case 'formula': { if (!value) break; - let range = this.quill.getSelection(true); + const range = this.quill.getSelection(true); if (range != null) { - let index = range.index + range.length; - this.quill.insertEmbed(index, this.root.getAttribute('data-mode'), value, Emitter.sources.USER); + const index = range.index + range.length; + this.quill.insertEmbed( + index, + this.root.getAttribute('data-mode'), + value, + Emitter.sources.USER, + ); if (this.root.getAttribute('data-mode') === 'formula') { this.quill.insertText(index + 1, ' ', Emitter.sources.USER); } @@ -234,22 +297,26 @@ class BaseTooltip extends Tooltip { } } - function extractVideoUrl(url) { - let match = url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/) || - url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/); + let match = + url.match( + /^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtube\.com\/watch.*v=([a-zA-Z0-9_-]+)/, + ) || + url.match(/^(?:(https?):\/\/)?(?:(?:www|m)\.)?youtu\.be\/([a-zA-Z0-9_-]+)/); if (match) { - return (match[1] || 'https') + '://www.youtube.com/embed/' + match[2] + '?showinfo=0'; + return `${match[1] || + 'https'}://www.youtube.com/embed/${match[2]}?showinfo=0`; } - if (match = url.match(/^(?:(https?):\/\/)?(?:www\.)?vimeo\.com\/(\d+)/)) { // eslint-disable-line no-cond-assign - return (match[1] || 'https') + '://player.vimeo.com/video/' + match[2] + '/'; + // eslint-disable-next-line no-cond-assign + if ((match = url.match(/^(?:(https?):\/\/)?(?:www\.)?vimeo\.com\/(\d+)/))) { + return `${match[1] || 'https'}://player.vimeo.com/video/${match[2]}/`; } return url; } function fillSelect(select, values, defaultValue = false) { - values.forEach(function(value) { - let option = document.createElement('option'); + values.forEach(value => { + const option = document.createElement('option'); if (value === defaultValue) { option.setAttribute('selected', 'selected'); } else { @@ -259,5 +326,4 @@ function fillSelect(select, values, defaultValue = false) { }); } - export { BaseTooltip, BaseTheme as default }; diff --git a/themes/bubble.js b/themes/bubble.js index 63f9325297..9978c68522 100644 --- a/themes/bubble.js +++ b/themes/bubble.js @@ -4,70 +4,49 @@ import BaseTheme, { BaseTooltip } from './base'; import { Range } from '../core/selection'; import icons from '../ui/icons'; - const TOOLBAR_CONFIG = [ ['bold', 'italic', 'link'], - [{ header: 1 }, { header: 2 }, 'blockquote'] + [{ header: 1 }, { header: 2 }, 'blockquote'], ]; -class BubbleTheme extends BaseTheme { - constructor(quill, options) { - if (options.modules.toolbar != null && options.modules.toolbar.container == null) { - options.modules.toolbar.container = TOOLBAR_CONFIG; - } - super(quill, options); - this.quill.container.classList.add('ql-bubble'); - } - - extendToolbar(toolbar) { - this.tooltip = new BubbleTooltip(this.quill, this.options.bounds); - this.tooltip.root.appendChild(toolbar.container); - this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), icons); - this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), icons); - } -} -BubbleTheme.DEFAULTS = extend(true, {}, BaseTheme.DEFAULTS, { - modules: { - toolbar: { - handlers: { - link: function(value) { - if (!value) { - this.quill.format('link', false); - } else { - this.quill.theme.tooltip.edit(); - } - } - } - } - } -}); - - class BubbleTooltip extends BaseTooltip { constructor(quill, bounds) { super(quill, bounds); - this.quill.on(Emitter.events.EDITOR_CHANGE, (type, range, oldRange, source) => { - if (type !== Emitter.events.SELECTION_CHANGE) return; - if (range != null && range.length > 0 && source === Emitter.sources.USER) { - this.show(); - // Lock our width so we will expand beyond our offsetParent boundaries - this.root.style.left = '0px'; - this.root.style.width = ''; - this.root.style.width = this.root.offsetWidth + 'px'; - let lines = this.quill.getLines(range.index, range.length); - if (lines.length === 1) { - this.position(this.quill.getBounds(range)); - } else { - let lastLine = lines[lines.length - 1]; - let index = this.quill.getIndex(lastLine); - let length = Math.min(lastLine.length() - 1, range.index + range.length - index); - let bounds = this.quill.getBounds(new Range(index, length)); - this.position(bounds); + this.quill.on( + Emitter.events.EDITOR_CHANGE, + (type, range, oldRange, source) => { + if (type !== Emitter.events.SELECTION_CHANGE) return; + if ( + range != null && + range.length > 0 && + source === Emitter.sources.USER + ) { + this.show(); + // Lock our width so we will expand beyond our offsetParent boundaries + this.root.style.left = '0px'; + this.root.style.width = ''; + this.root.style.width = `${this.root.offsetWidth}px`; + const lines = this.quill.getLines(range.index, range.length); + if (lines.length === 1) { + this.position(this.quill.getBounds(range)); + } else { + const lastLine = lines[lines.length - 1]; + const index = this.quill.getIndex(lastLine); + const length = Math.min( + lastLine.length() - 1, + range.index + range.length - index, + ); + const indexBounds = this.quill.getBounds(new Range(index, length)); + this.position(indexBounds); + } + } else if ( + document.activeElement !== this.textbox && + this.quill.hasFocus() + ) { + this.hide(); } - } else if (document.activeElement !== this.textbox && this.quill.hasFocus()) { - this.hide(); - } - }); + }, + ); } listen() { @@ -79,7 +58,7 @@ class BubbleTooltip extends BaseTooltip { // Let selection be restored by toolbar handlers before repositioning setTimeout(() => { if (this.root.classList.contains('ql-hidden')) return; - let range = this.quill.getSelection(); + const range = this.quill.getSelection(); if (range != null) { this.position(this.quill.getBounds(range)); } @@ -92,20 +71,62 @@ class BubbleTooltip extends BaseTooltip { } position(reference) { - let shift = super.position(reference); - let arrow = this.root.querySelector('.ql-tooltip-arrow'); + const shift = super.position(reference); + const arrow = this.root.querySelector('.ql-tooltip-arrow'); arrow.style.marginLeft = ''; - if (shift === 0) return shift; - arrow.style.marginLeft = (-1*shift - arrow.offsetWidth/2) + 'px'; + if (shift !== 0) { + arrow.style.marginLeft = `${-1 * shift - arrow.offsetWidth / 2}px`; + } + return shift; } } BubbleTooltip.TEMPLATE = [ '', '' + '', + '', + '', ].join(''); +class BubbleTheme extends BaseTheme { + constructor(quill, options) { + if ( + options.modules.toolbar != null && + options.modules.toolbar.container == null + ) { + options.modules.toolbar.container = TOOLBAR_CONFIG; + } + super(quill, options); + this.quill.container.classList.add('ql-bubble'); + } + + extendToolbar(toolbar) { + this.tooltip = new BubbleTooltip(this.quill, this.options.bounds); + this.tooltip.root.appendChild(toolbar.container); + this.buildButtons( + [].slice.call(toolbar.container.querySelectorAll('button')), + icons, + ); + this.buildPickers( + [].slice.call(toolbar.container.querySelectorAll('select')), + icons, + ); + } +} +BubbleTheme.DEFAULTS = extend(true, {}, BaseTheme.DEFAULTS, { + modules: { + toolbar: { + handlers: { + link: value => { + if (!value) { + this.quill.format('link', false); + } else { + this.quill.theme.tooltip.edit(); + } + }, + }, + }, + }, +}); export { BubbleTooltip, BubbleTheme as default }; diff --git a/themes/snow.js b/themes/snow.js index 713cc6e890..d189b949af 100644 --- a/themes/snow.js +++ b/themes/snow.js @@ -5,59 +5,13 @@ import LinkBlot from '../formats/link'; import { Range } from '../core/selection'; import icons from '../ui/icons'; - const TOOLBAR_CONFIG = [ [{ header: ['1', '2', '3', false] }], ['bold', 'italic', 'underline', 'link'], [{ list: 'ordered' }, { list: 'bullet' }], - ['clean'] + ['clean'], ]; -class SnowTheme extends BaseTheme { - constructor(quill, options) { - if (options.modules.toolbar != null && options.modules.toolbar.container == null) { - options.modules.toolbar.container = TOOLBAR_CONFIG; - } - super(quill, options); - this.quill.container.classList.add('ql-snow'); - } - - extendToolbar(toolbar) { - toolbar.container.classList.add('ql-snow'); - this.buildButtons([].slice.call(toolbar.container.querySelectorAll('button')), icons); - this.buildPickers([].slice.call(toolbar.container.querySelectorAll('select')), icons); - this.tooltip = new SnowTooltip(this.quill, this.options.bounds); - if (toolbar.container.querySelector('.ql-link')) { - this.quill.keyboard.addBinding({ key: 'k', shortKey: true }, function(range, context) { - toolbar.handlers['link'].call(toolbar, !context.format.link); - }); - } - } -} -SnowTheme.DEFAULTS = extend(true, {}, BaseTheme.DEFAULTS, { - modules: { - toolbar: { - handlers: { - link: function(value) { - if (value) { - let range = this.quill.getSelection(); - if (range == null || range.length == 0) return; - let preview = this.quill.getText(range); - if (/^\S+@\S+\.\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) { - preview = 'mailto:' + preview; - } - let tooltip = this.quill.theme.tooltip; - tooltip.edit('link', preview); - } else { - this.quill.format('link', false); - } - } - } - } - } -}); - - class SnowTooltip extends BaseTooltip { constructor(quill, bounds) { super(quill, bounds); @@ -66,7 +20,7 @@ class SnowTooltip extends BaseTooltip { listen() { super.listen(); - this.root.querySelector('a.ql-action').addEventListener('click', (event) => { + this.root.querySelector('a.ql-action').addEventListener('click', event => { if (this.root.classList.contains('ql-editing')) { this.save(); } else { @@ -74,9 +28,9 @@ class SnowTooltip extends BaseTooltip { } event.preventDefault(); }); - this.root.querySelector('a.ql-remove').addEventListener('click', (event) => { + this.root.querySelector('a.ql-remove').addEventListener('click', event => { if (this.linkRange != null) { - let range = this.linkRange; + const range = this.linkRange; this.restoreFocus(); this.quill.formatText(range, 'link', false, Emitter.sources.USER); delete this.linkRange; @@ -84,24 +38,30 @@ class SnowTooltip extends BaseTooltip { event.preventDefault(); this.hide(); }); - this.quill.on(Emitter.events.SELECTION_CHANGE, (range, oldRange, source) => { - if (range == null) return; - if (range.length === 0 && source === Emitter.sources.USER) { - let [link, offset] = this.quill.scroll.descendant(LinkBlot, range.index); - if (link != null) { - this.linkRange = new Range(range.index - offset, link.length()); - let preview = LinkBlot.formats(link.domNode); - this.preview.textContent = preview; - this.preview.setAttribute('href', preview); - this.show(); - this.position(this.quill.getBounds(this.linkRange)); - return; + this.quill.on( + Emitter.events.SELECTION_CHANGE, + (range, oldRange, source) => { + if (range == null) return; + if (range.length === 0 && source === Emitter.sources.USER) { + const [link, offset] = this.quill.scroll.descendant( + LinkBlot, + range.index, + ); + if (link != null) { + this.linkRange = new Range(range.index - offset, link.length()); + const preview = LinkBlot.formats(link.domNode); + this.preview.textContent = preview; + this.preview.setAttribute('href', preview); + this.show(); + this.position(this.quill.getBounds(this.linkRange)); + return; + } + } else { + delete this.linkRange; } - } else { - delete this.linkRange; - } - this.hide(); - }); + this.hide(); + }, + ); } show() { @@ -113,8 +73,66 @@ SnowTooltip.TEMPLATE = [ '', '', '', - '' + '', ].join(''); +class SnowTheme extends BaseTheme { + constructor(quill, options) { + if ( + options.modules.toolbar != null && + options.modules.toolbar.container == null + ) { + options.modules.toolbar.container = TOOLBAR_CONFIG; + } + super(quill, options); + this.quill.container.classList.add('ql-snow'); + } + + extendToolbar(toolbar) { + toolbar.container.classList.add('ql-snow'); + this.buildButtons( + [].slice.call(toolbar.container.querySelectorAll('button')), + icons, + ); + this.buildPickers( + [].slice.call(toolbar.container.querySelectorAll('select')), + icons, + ); + this.tooltip = new SnowTooltip(this.quill, this.options.bounds); + if (toolbar.container.querySelector('.ql-link')) { + this.quill.keyboard.addBinding( + { key: 'k', shortKey: true }, + (range, context) => { + toolbar.handlers.link.call(toolbar, !context.format.link); + }, + ); + } + } +} +SnowTheme.DEFAULTS = extend(true, {}, BaseTheme.DEFAULTS, { + modules: { + toolbar: { + handlers: { + link: value => { + if (value) { + const range = this.quill.getSelection(); + if (range == null || range.length === 0) return; + let preview = this.quill.getText(range); + if ( + /^\S+@\S+\.\S+$/.test(preview) && + preview.indexOf('mailto:') !== 0 + ) { + preview = `mailto:${preview}`; + } + const { tooltip } = this.quill.theme; + tooltip.edit('link', preview); + } else { + this.quill.format('link', false); + } + }, + }, + }, + }, +}); export default SnowTheme; diff --git a/ui/color-picker.js b/ui/color-picker.js index b1f85dc2cb..9dc259b15a 100644 --- a/ui/color-picker.js +++ b/ui/color-picker.js @@ -1,26 +1,27 @@ import Picker from './picker'; - class ColorPicker extends Picker { constructor(select, label) { super(select); this.label.innerHTML = label; this.container.classList.add('ql-color-picker'); - [].slice.call(this.container.querySelectorAll('.ql-picker-item'), 0, 7).forEach(function(item) { - item.classList.add('ql-primary'); - }); + [].slice + .call(this.container.querySelectorAll('.ql-picker-item'), 0, 7) + .forEach(item => { + item.classList.add('ql-primary'); + }); } buildItem(option) { - let item = super.buildItem(option); + const item = super.buildItem(option); item.style.backgroundColor = option.getAttribute('value') || ''; return item; } selectItem(item, trigger) { super.selectItem(item, trigger); - let colorLabel = this.label.querySelector('.ql-color-label'); - let value = item ? item.getAttribute('data-value') || '' : ''; + const colorLabel = this.label.querySelector('.ql-color-label'); + const value = item ? item.getAttribute('data-value') || '' : ''; if (colorLabel) { if (colorLabel.tagName === 'line') { colorLabel.style.stroke = value; @@ -31,5 +32,4 @@ class ColorPicker extends Picker { } } - export default ColorPicker; diff --git a/ui/icon-picker.js b/ui/icon-picker.js index 1a8ee27b33..b568736ffb 100644 --- a/ui/icon-picker.js +++ b/ui/icon-picker.js @@ -1,23 +1,24 @@ import Picker from './picker'; - class IconPicker extends Picker { constructor(select, icons) { super(select); this.container.classList.add('ql-icon-picker'); - [].forEach.call(this.container.querySelectorAll('.ql-picker-item'), (item) => { - item.innerHTML = icons[item.getAttribute('data-value') || '']; - }); + [].forEach.call( + this.container.querySelectorAll('.ql-picker-item'), + item => { + item.innerHTML = icons[item.getAttribute('data-value') || '']; + }, + ); this.defaultItem = this.container.querySelector('.ql-selected'); this.selectItem(this.defaultItem); } - selectItem(item, trigger) { - super.selectItem(item, trigger); - item = item || this.defaultItem; + selectItem(target, trigger) { + super.selectItem(target, trigger); + const item = target || this.defaultItem; this.label.innerHTML = item.innerHTML; } } - export default IconPicker; diff --git a/ui/icons.js b/ui/icons.js index 7616b9a11d..0d957acdfb 100644 --- a/ui/icons.js +++ b/ui/icons.js @@ -1,49 +1,72 @@ +import alignLeftIcon from '../assets/icons/align-left.svg'; +import alignCenterIcon from '../assets/icons/align-center.svg'; +import alignRightIcon from '../assets/icons/align-right.svg'; +import alignJustifyIcon from '../assets/icons/align-justify.svg'; +import backgroundIcon from '../assets/icons/background.svg'; +import blockquoteIcon from '../assets/icons/blockquote.svg'; +import boldIcon from '../assets/icons/bold.svg'; +import cleanIcon from '../assets/icons/clean.svg'; +import codeIcon from '../assets/icons/code.svg'; +import colorIcon from '../assets/icons/color.svg'; +import directionLeftToRightIcon from '../assets/icons/direction-ltr.svg'; +import directionRightToLeftIcon from '../assets/icons/direction-rtl.svg'; +import formulaIcon from '../assets/icons/formula.svg'; +import headerIcon from '../assets/icons/header.svg'; +import header2Icon from '../assets/icons/header-2.svg'; +import italicIcon from '../assets/icons/italic.svg'; +import imageIcon from '../assets/icons/image.svg'; +import indentIcon from '../assets/icons/indent.svg'; +import outdentIcon from '../assets/icons/outdent.svg'; +import linkIcon from '../assets/icons/link.svg'; +import listBulletIcon from '../assets/icons/list-bullet.svg'; +import listCheckIcon from '../assets/icons/list-check.svg'; +import listOrderedIcon from '../assets/icons/list-ordered.svg'; +import subscriptIcon from '../assets/icons/subscript.svg'; +import superscriptIcon from '../assets/icons/superscript.svg'; +import strikeIcon from '../assets/icons/strike.svg'; +import underlineIcon from '../assets/icons/underline.svg'; +import videoIcon from '../assets/icons/video.svg'; + module.exports = { - 'align': { - '' : require('../assets/icons/align-left.svg'), - 'center' : require('../assets/icons/align-center.svg'), - 'right' : require('../assets/icons/align-right.svg'), - 'justify' : require('../assets/icons/align-justify.svg') + align: { + '': alignLeftIcon, + center: alignCenterIcon, + right: alignRightIcon, + justify: alignJustifyIcon, }, - 'background': require('../assets/icons/background.svg'), - 'blockquote': require('../assets/icons/blockquote.svg'), - 'bold' : require('../assets/icons/bold.svg'), - 'clean' : require('../assets/icons/clean.svg'), - 'code' : require('../assets/icons/code.svg'), - 'code-block': require('../assets/icons/code.svg'), - 'color' : require('../assets/icons/color.svg'), - 'direction' : { - '' : require('../assets/icons/direction-ltr.svg'), - 'rtl' : require('../assets/icons/direction-rtl.svg') + background: backgroundIcon, + blockquote: blockquoteIcon, + bold: boldIcon, + clean: cleanIcon, + code: codeIcon, + 'code-block': codeIcon, + color: colorIcon, + direction: { + '': directionLeftToRightIcon, + rtl: directionRightToLeftIcon, }, - 'float': { - 'center' : require('../assets/icons/float-center.svg'), - 'full' : require('../assets/icons/float-full.svg'), - 'left' : require('../assets/icons/float-left.svg'), - 'right' : require('../assets/icons/float-right.svg') + formula: formulaIcon, + header: { + '1': headerIcon, + '2': header2Icon, }, - 'formula' : require('../assets/icons/formula.svg'), - 'header': { - '1' : require('../assets/icons/header.svg'), - '2' : require('../assets/icons/header-2.svg') + italic: italicIcon, + image: imageIcon, + indent: { + '+1': indentIcon, + '-1': outdentIcon, }, - 'italic' : require('../assets/icons/italic.svg'), - 'image' : require('../assets/icons/image.svg'), - 'indent': { - '+1' : require('../assets/icons/indent.svg'), - '-1' : require('../assets/icons/outdent.svg') + link: linkIcon, + list: { + bullet: listBulletIcon, + check: listCheckIcon, + ordered: listOrderedIcon, }, - 'link' : require('../assets/icons/link.svg'), - 'list': { - 'ordered' : require('../assets/icons/list-ordered.svg'), - 'bullet' : require('../assets/icons/list-bullet.svg'), - 'check' : require('../assets/icons/list-check.svg') + script: { + sub: subscriptIcon, + super: superscriptIcon, }, - 'script': { - 'sub' : require('../assets/icons/subscript.svg'), - 'super' : require('../assets/icons/superscript.svg'), - }, - 'strike' : require('../assets/icons/strike.svg'), - 'underline' : require('../assets/icons/underline.svg'), - 'video' : require('../assets/icons/video.svg') + strike: strikeIcon, + underline: underlineIcon, + video: videoIcon, }; diff --git a/ui/picker.js b/ui/picker.js index 6597c653cf..9997276c7b 100644 --- a/ui/picker.js +++ b/ui/picker.js @@ -1,6 +1,5 @@ import DropdownIcon from '../assets/icons/dropdown.svg'; - class Picker { constructor(select) { this.select = select; @@ -15,7 +14,7 @@ class Picker { } buildItem(option) { - let item = document.createElement('span'); + const item = document.createElement('span'); item.classList.add('ql-picker-item'); if (option.hasAttribute('value')) { item.setAttribute('data-value', option.getAttribute('value')); @@ -30,7 +29,7 @@ class Picker { } buildLabel() { - let label = document.createElement('span'); + const label = document.createElement('span'); label.classList.add('ql-picker-label'); label.innerHTML = DropdownIcon; this.container.appendChild(label); @@ -38,10 +37,10 @@ class Picker { } buildOptions() { - let options = document.createElement('span'); + const options = document.createElement('span'); options.classList.add('ql-picker-options'); - [].slice.call(this.select.options).forEach((option) => { - let item = this.buildItem(option); + [].slice.call(this.select.options).forEach(option => { + const item = this.buildItem(option); options.appendChild(item); if (option.selected === true) { this.selectItem(item); @@ -51,7 +50,7 @@ class Picker { } buildPicker() { - [].slice.call(this.select.attributes).forEach((item) => { + [].slice.call(this.select.attributes).forEach(item => { this.container.setAttribute(item.name, item.value); }); this.container.classList.add('ql-picker'); @@ -64,7 +63,7 @@ class Picker { } selectItem(item, trigger = false) { - let selected = this.container.querySelector('.ql-selected'); + const selected = this.container.querySelector('.ql-selected'); if (item === selected) return; if (selected != null) { selected.classList.remove('ql-selected'); @@ -85,8 +84,9 @@ class Picker { if (trigger) { if (typeof Event === 'function') { this.select.dispatchEvent(new Event('change')); - } else if (typeof Event === 'object') { // IE11 - let event = document.createEvent('Event'); + } else if (typeof Event === 'object') { + // IE11 + const event = document.createEvent('Event'); event.initEvent('change', true, true); this.select.dispatchEvent(event); } @@ -97,16 +97,19 @@ class Picker { update() { let option; if (this.select.selectedIndex > -1) { - let item = this.container.querySelector('.ql-picker-options').children[this.select.selectedIndex]; + const item = this.container.querySelector('.ql-picker-options').children[ + this.select.selectedIndex + ]; option = this.select.options[this.select.selectedIndex]; this.selectItem(item); } else { this.selectItem(null); } - let isActive = option != null && option !== this.select.querySelector('option[selected]'); + const isActive = + option != null && + option !== this.select.querySelector('option[selected]'); this.label.classList.toggle('ql-active', isActive); } } - export default Picker; diff --git a/ui/tooltip.js b/ui/tooltip.js index 3a72736186..5e312e167e 100644 --- a/ui/tooltip.js +++ b/ui/tooltip.js @@ -6,7 +6,7 @@ class Tooltip { this.root.innerHTML = this.constructor.TEMPLATE; if (this.quill.root === this.quill.scrollingContainer) { this.quill.root.addEventListener('scroll', () => { - this.root.style.marginTop = (-1*this.quill.root.scrollTop) + 'px'; + this.root.style.marginTop = `${-1 * this.quill.root.scrollTop}px`; }); } this.hide(); @@ -17,27 +17,28 @@ class Tooltip { } position(reference) { - let left = reference.left + reference.width/2 - this.root.offsetWidth/2; + const left = + reference.left + reference.width / 2 - this.root.offsetWidth / 2; // root.scrollTop should be 0 if scrollContainer !== root - let top = reference.bottom + this.quill.root.scrollTop; - this.root.style.left = left + 'px'; - this.root.style.top = top + 'px'; + const top = reference.bottom + this.quill.root.scrollTop; + this.root.style.left = `${left}px`; + this.root.style.top = `${top}px`; this.root.classList.remove('ql-flip'); - let containerBounds = this.boundsContainer.getBoundingClientRect(); - let rootBounds = this.root.getBoundingClientRect(); + const containerBounds = this.boundsContainer.getBoundingClientRect(); + const rootBounds = this.root.getBoundingClientRect(); let shift = 0; if (rootBounds.right > containerBounds.right) { shift = containerBounds.right - rootBounds.right; - this.root.style.left = (left + shift) + 'px'; + this.root.style.left = `${left + shift}px`; } if (rootBounds.left < containerBounds.left) { shift = containerBounds.left - rootBounds.left; - this.root.style.left = (left + shift) + 'px'; + this.root.style.left = `${left + shift}px`; } if (rootBounds.bottom > containerBounds.bottom) { - let height = rootBounds.bottom - rootBounds.top; - let verticalShift = reference.bottom - reference.top + height; - this.root.style.top = (top - verticalShift) + 'px'; + const height = rootBounds.bottom - rootBounds.top; + const verticalShift = reference.bottom - reference.top + height; + this.root.style.top = `${top - verticalShift}px`; this.root.classList.add('ql-flip'); } return shift; @@ -49,5 +50,4 @@ class Tooltip { } } - export default Tooltip;