From 889869e9ca2d023dfb558b705f9ebaa1c78479f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Piotrek=20Koszuli=C5=84ski?=
Date: Mon, 31 Oct 2016 19:18:26 +0100
Subject: [PATCH] Used a build which works around
https://github.com/ckeditor/ckeditor5-heading/issues/40.
---
lib/ckeditor5/ckeditor.js | 14044 ++++++++++++++++----------------
lib/ckeditor5/ckeditor.min.js | 20 +-
2 files changed, 7032 insertions(+), 7032 deletions(-)
diff --git a/lib/ckeditor5/ckeditor.js b/lib/ckeditor5/ckeditor.js
index 26bda57fab1..01eb9499fdf 100644
--- a/lib/ckeditor5/ckeditor.js
+++ b/lib/ckeditor5/ckeditor.js
@@ -42220,180 +42220,377 @@ var ClassicEditor$1 = function (_StandardEditor) {
*/
/**
- * The block autoformatting engine. Allows to format various block patterns. For example,
- * it can be configured to make a paragraph starting with "* " a list item.
- *
- * The autoformatting operation is integrated with the undo manager,
- * so the autoformatting step can be undone, if the user's intention wasn't to format the text.
+ * The base class for CKEditor feature classes. Features are main way to enhance CKEditor abilities with tools,
+ * utilities, services and components.
*
- * See the constructors documentation to learn how to create custom inline autoformatters. You can also use
- * the {@link autoformat.Autoformat} feature which enables a set of default autoformatters (lists, headings, bold and italic).
+ * The main responsibilities for Feature are:
+ * * setting required dependencies (see {@link core.Plugin#requires},
+ * * configuring, instantiating and registering commands to editor,
+ * * registering converters to editor (if the feature operates on Tree Model),
+ * * setting and registering UI components (if the feature uses it).
*
- * @memberOf autoformat
+ * @memberOf core
*/
-var BlockAutoformatEngine =
+var Feature = function (_Plugin) {
+ inherits(Feature, _Plugin);
+
+ function Feature() {
+ classCallCheck(this, Feature);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Feature).apply(this, arguments));
+ }
+
+ return Feature;
+}(Plugin);
+
/**
- * Creates listener triggered on `change` event in document.
- * Calls callback when inserted text matches regular expression or command name
- * if provided instead of callback.
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * Provides chainable, high-level API to easily build basic model-to-view converters that are appended to given
+ * dispatchers. In many cases, this is the API that should be used to specify how abstract model elements and
+ * attributes should be represented in the view (and then later in DOM). Instances of this class are created by
+ * {@link engine.conversion.buildModelConverter}.
*
- * Examples of usage:
+ * If you need more complex converters, see {@link engine.conversion.ModelConversionDispatcher},
+ * {@link engine.conversion.modelToView}, {@link engine.conversion.ModelConsumable}, {@link engine.conversion.Mapper}.
*
- * To convert paragraph to heading1 when `- ` is typed, using just commmand name:
+ * Using this API it is possible to create three kinds of converters:
*
- * new BlockAutoformatEngine( editor, /^\- $/, 'heading1' );
+ * 1. Model element to view element converter. This is a converter that takes the model element and represents it
+ * in the view.
*
- * To convert paragraph to heading1 when `- ` is typed, using just callback:
+ * buildModelConverter().for( dispatcher ).fromElement( 'paragraph' ).toElement( 'p' );
+ * buildModelConverter().for( dispatcher ).fromElement( 'image' ).toElement( 'img' );
*
- * new BlockAutoformatEngine( editor, /^\- $/, ( context ) => {
- * const { batch, match } = context;
- * const headingLevel = match[ 1 ].length;
+ * 2. Model attribute to view attribute converter. This is a converter that operates on model element attributes
+ * and converts them to view element attributes. It is suitable for elements like `image` (`src`, `title` attributes).
*
- * editor.execute( 'heading', {
- * batch,
- * formatId: `heading${ headingLevel }`
- * } );
- * } );
+ * buildModelConverter().for( dispatcher ).fromElement( 'image' ).toElement( 'img' );
+ * buildModelConverter().for( dispatcher ).fromAttribute( 'src' ).toAttribute();
*
- * @param {core.editor.Editor} editor Editor instance.
- * @param {RegExp} pattern Regular expression to exec on just inserted text.
- * @param {Function|String} callbackOrCommand Callback to execute or command to run when text is matched.
- * In case of providing callback it receives following parameters:
- * * {engine.model.Batch} batch Newly created batch for autoformat changes.
- * * {Object} match RegExp.exec() result of matching pattern to inserted text.
+ * 3. Model attribute to view element converter. This is a converter that takes model attributes and represents them
+ * as view elements. Elements created by this kind of converter are wrapping other view elements. Wrapped view nodes
+ * correspond to model nodes had converter attribute. It is suitable for attributes like `bold`, where `bold` attribute
+ * set on model text nodes is converter to `strong` view element.
+ *
+ * buildModelConverter().for( dispatcher ).fromAttribute( 'bold' ).toElement( 'strong' );
+ *
+ * It is possible to provide various different parameters for {@link engine.conversion.ModelConverterBuilder#toElement}
+ * and {@link engine.conversion.ModelConverterBuilder#toAttribute} methods. See their descriptions to learn more.
+ *
+ * It is also possible to {@link engine.conversion.ModelConverterBuilder#withPriority change default priority}
+ * of created converters to decide which converter should be fired earlier and which later. This is useful if you have
+ * a general converter but also want to provide different special-case converters (i.e. given model element is converted
+ * always to given view element, but if it has given attribute it is converter to other view element). For this,
+ * use {@link engine.conversion.ModelConverterBuilder#withPriority withPriority} right after `from...` method.
+ *
+ * Note that `to...` methods are "terminators", which means that should be the last one used in building converter.
+ *
+ * You can use {@link engine.conversion.ViewConverterBuilder} to create "opposite" converters - from view to model.
+ *
+ * @memberOf engine.conversion
*/
-function BlockAutoformatEngine(editor, pattern, callbackOrCommand) {
- classCallCheck(this, BlockAutoformatEngine);
-
- var callback = void 0;
-
- if (typeof callbackOrCommand == 'function') {
- callback = callbackOrCommand;
- } else {
- (function () {
- // We assume that the actual command name was provided.
- var command = callbackOrCommand;
- callback = function callback(context) {
- var batch = context.batch;
+var ModelConverterBuilder = function () {
+ /**
+ * Creates `ModelConverterBuilder` with given `dispatchers` registered to it.
+ */
+ function ModelConverterBuilder() {
+ classCallCheck(this, ModelConverterBuilder);
- // Create new batch for removal and command execution.
+ /**
+ * Dispatchers to which converters will be attached.
+ *
+ * @type {Array.}
+ * @private
+ */
+ this._dispatchers = [];
- editor.execute(command, { batch: batch });
- };
- })();
+ /**
+ * Contains data about registered "from" query.
+ *
+ * @type {Object}
+ * @private
+ */
+ this._from = null;
}
- editor.document.on('change', function (event, type, changes) {
- if (type != 'insert') {
- return;
- }
-
- // Take the first element. Typing shouldn't add more than one element at once.
- // And if it is not typing (e.g. paste), Autoformat should not be fired.
- var value = changes.range.getItems().next().value;
-
- if (!(value instanceof TextProxy)) {
- return;
- }
+ /**
+ * Set one or more dispatchers which the built converter will be attached to.
+ *
+ * @chainable
+ * @param {...engine.conversion.ModelConversionDispatcher} dispatchers One or more dispatchers.
+ * @returns {engine.conversion.ModelConverterBuilder}
+ */
- var textNode = value.textNode;
- var text = textNode.data;
- // Run matching only on non-empty paragraphs.
- if (textNode.parent.name !== 'paragraph' || !text) {
- return;
- }
+ createClass(ModelConverterBuilder, [{
+ key: 'for',
+ value: function _for() {
+ for (var _len = arguments.length, dispatchers = Array(_len), _key = 0; _key < _len; _key++) {
+ dispatchers[_key] = arguments[_key];
+ }
- var match = pattern.exec(text);
+ this._dispatchers = dispatchers;
- if (!match) {
- return;
+ return this;
}
- editor.document.enqueueChanges(function () {
- // Create new batch to separate typing batch from the Autoformat changes.
- var batch = editor.document.batch();
+ /**
+ * Registers what model element should be converted.
+ *
+ * @chainable
+ * @param {String} elementName Name of element to convert.
+ * @returns {engine.conversion.ModelConverterBuilder}
+ */
- // Matched range.
- var range = Range$1.createFromParentsAndOffsets(textNode.parent, 0, textNode.parent, match[0].length);
+ }, {
+ key: 'fromElement',
+ value: function fromElement(elementName) {
+ this._from = {
+ type: 'element',
+ name: elementName,
+ priority: null
+ };
- // Remove matched text.
- batch.remove(range);
+ return this;
+ }
- callback({ batch: batch, match: match });
- });
- });
-};
+ /**
+ * Registers what model attribute should be converted.
+ *
+ * @chainable
+ * @param {String} key Key of attribute to convert.
+ * @returns {engine.conversion.ModelConverterBuilder}
+ */
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ }, {
+ key: 'fromAttribute',
+ value: function fromAttribute(key) {
+ this._from = {
+ type: 'attribute',
+ key: key,
+ priority: null
+ };
-/**
- * Walks through given array of ranges and removes parts of them that are not allowed by passed schema to have the
- * attribute set. This is done by breaking a range in two and omitting the not allowed part.
- *
- * @param {String} attribute Attribute key.
- * @param {Array.} ranges Ranges to be validated.
- * @param {engine.model.Schema} schema Document schema.
- * @returns {Array.} Ranges without invalid parts.
- */
-function getSchemaValidRanges(attribute, ranges, schema) {
- var validRanges = [];
+ return this;
+ }
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+ /**
+ * Changes default priority for built converter. The lower the number, the earlier converter will be fired.
+ * Default priority is `10`.
+ *
+ * **Note:** Keep in mind that event priority, that is set by this modifier, is used for attribute priority
+ * when {@link engine.view.writer} is used. This changes how view elements are ordered,
+ * i.e.: `foo ` vs `foo `. Using priority you can also
+ * prevent node merging, i.e.: `foo ` vs `foo `.
+ * If you want to prevent merging, just set different priority for both converters.
+ *
+ * buildModelConverter().for( dispatcher ).fromAttribute( 'bold' ).withPriority( 2 ).toElement( 'strong' );
+ * buildModelConverter().for( dispatcher ).fromAttribute( 'italic' ).withPriority( 3 ).toElement( 'em' );
+ *
+ * @chainable
+ * @param {Number} priority Converter priority.
+ * @returns {engine.conversion.ModelConverterBuilder}
+ */
- try {
- for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var range = _step.value;
+ }, {
+ key: 'withPriority',
+ value: function withPriority(priority) {
+ this._from.priority = priority;
- var walker = new TreeWalker({ boundaries: range, mergeCharacters: true });
- var step = walker.next();
+ return this;
+ }
- var last = range.start;
- var from = range.start;
- var to = range.end;
+ /**
+ * Registers what view element will be created by converter.
+ *
+ * Method accepts various ways of providing how the view element will be created. You can pass view element name as
+ * `string`, view element instance which will be cloned and used, or creator function which returns view element that
+ * will be used. Keep in mind that when you view element instance or creator function, it has to be/return a
+ * proper type of view element: {@link engine.view.ViewContainerElement ViewContainerElement} if you convert
+ * from element or {@link engine.view.ViewAttributeElement ViewAttributeElement} if you convert from attribute.
+ *
+ * buildModelConverter().for( dispatcher ).fromElement( 'paragraph' ).toElement( 'p' );
+ *
+ * buildModelConverter().for( dispatcher ).fromElement( 'image' ).toElement( new ViewContainerElement( 'img' ) );
+ *
+ * buildModelConverter().for( dispatcher )
+ * .fromElement( 'header' )
+ * .toElement( ( data ) => new ViewContainerElement( 'h' + data.item.getAttribute( 'level' ) ) );
+ *
+ * buildModelConverter().for( dispatcher ).fromAttribute( 'bold' ).toElement( new ViewAttributeElement( 'strong' ) );
+ *
+ * Creator function will be passed different values depending whether conversion is from element or from attribute:
+ *
+ * * from element: dispatcher's {@link engine.conversion.ModelConversionDispatcher#event:insert insert event} parameters
+ * will be passed,
+ * * from attribute: there is one parameter and it is attribute value.
+ *
+ * This method also registers model selection to view selection converter, if conversion is from attribute.
+ *
+ * This method creates the converter and adds it as a callback to a proper
+ * {@link engine.conversion.ModelConversionDispatcher conversion dispatcher} event.
+ *
+ * @param {String|engine.view.ViewElement|Function} element Element created by converter.
+ */
- while (!step.done) {
- var name = step.value.item.name || '$text';
+ }, {
+ key: 'toElement',
+ value: function toElement(element) {
+ var priority = this._from.priority === null ? 'normal' : this._from.priority;
- if (!schema.check({ name: name, inside: last, attributes: attribute })) {
- if (!from.isEqual(last)) {
- validRanges.push(new Range$1(from, last));
- }
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- from = walker.position;
- }
+ try {
+ for (var _iterator = this._dispatchers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var dispatcher = _step.value;
- last = walker.position;
- step = walker.next();
- }
+ if (this._from.type == 'element') {
+ // From model element to view element -> insert element.
+ element = typeof element == 'string' ? new ContainerElement(element) : element;
- if (from && !from.isEqual(to)) {
- validRanges.push(new Range$1(from, to));
+ dispatcher.on('insert:' + this._from.name, insertElement(element), { priority: priority });
+ } else {
+ // From model attribute to view element -> wrap and unwrap.
+ element = typeof element == 'string' ? new AttributeElement(element) : element;
+
+ dispatcher.on('addAttribute:' + this._from.key, wrap(element), { priority: priority });
+ dispatcher.on('changeAttribute:' + this._from.key, wrap(element), { priority: priority });
+ dispatcher.on('removeAttribute:' + this._from.key, unwrap(element), { priority: priority });
+
+ dispatcher.on('selectionAttribute:' + this._from.key, convertSelectionAttribute(element), { priority: priority });
+ }
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
}
}
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
+
+ /**
+ * Registers what view attribute will be created by converter. Keep in mind, that only model attribute to
+ * view attribute conversion is supported.
+ *
+ * Method accepts various ways of providing how the view attribute will be created:
+ *
+ * * for no passed parameter, attribute key and value will be converted 1-to-1 to view attribute,
+ * * if you pass one `string`, it will be used as new attribute key while attribute value will be copied,
+ * * if you pass two `string`s, first one will be used as new attribute key and second one as new attribute value,
+ * * if you pass a function, it is expected to return an object with `key` and `value` properties representing attribute key and value.
+ * This function will be passed model attribute value and model attribute key as first two parameters and then
+ * all dispatcher's {engine.conversion.ModelConversionDispatcher#event:changeAttribute changeAttribute event} parameters.
+ *
+ * buildModelConverter().for( dispatcher ).fromAttribute( 'class' ).toAttribute( '' );
+ *
+ * buildModelConverter().for( dispatcher ).fromAttribute( 'linkTitle' ).toAttribute( 'title' );
+ *
+ * buildModelConverter().for( dispatcher ).fromAttribute( 'highlighted' ).toAttribute( 'style', 'background:yellow' );
+ *
+ * buildModelConverter().for( dispatcher )
+ * .fromAttribute( 'theme' )
+ * .toAttribute( ( value ) => ( { key: 'class', value: value + '-theme' } ) );
+ *
+ * This method creates the converter and adds it as a callback to a proper
+ * {@link engine.conversion.ModelConversionDispatcher conversion dispatcher} event.
+ *
+ * @param {String|Function} [keyOrCreator] Attribute key or a creator function.
+ * @param {*} [value] Attribute value.
+ */
+
+ }, {
+ key: 'toAttribute',
+ value: function toAttribute(keyOrCreator, value) {
+ if (this._from.type == 'element') {
+ // Converting from model element to view attribute is unsupported.
+ return;
}
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
+
+ var attributeCreator = void 0;
+
+ if (!keyOrCreator) {
+ // If `keyOrCreator` is not set, we assume default behavior which is 1:1 attribute re-write.
+ // This is also a default behavior for `setAttribute` converter when no attribute creator is passed.
+ attributeCreator = undefined;
+ } else if (typeof keyOrCreator == 'string') {
+ // `keyOrCreator` is an attribute key.
+
+ if (value) {
+ // If value is set, create "dumb" creator that always returns the same object.
+ attributeCreator = function attributeCreator() {
+ return { key: keyOrCreator, value: value };
+ };
+ } else {
+ // If value is not set, take it from the passed parameter.
+ attributeCreator = function attributeCreator(value) {
+ return { key: keyOrCreator, value: value };
+ };
+ }
+ } else {
+ // `keyOrCreator` is an attribute creator function.
+ attributeCreator = keyOrCreator;
+ }
+
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+
+ try {
+ for (var _iterator2 = this._dispatchers[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var dispatcher = _step2.value;
+
+ var options = { priority: this._from.priority || 'normal' };
+
+ dispatcher.on('addAttribute:' + this._from.key, setAttribute(attributeCreator), options);
+ dispatcher.on('changeAttribute:' + this._from.key, setAttribute(attributeCreator), options);
+ dispatcher.on('removeAttribute:' + this._from.key, removeAttribute(attributeCreator), options);
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
}
}
- }
+ }]);
+ return ModelConverterBuilder;
+}();
- return validRanges;
+/**
+ * Entry point for model-to-view converters builder. This chainable API makes it easy to create basic, most common
+ * model-to-view converters and attach them to provided dispatchers. The method returns an instance of
+ * {@link engine.conversion.ModelConverterBuilder}.
+ *
+ * @external engine.conversion.buildModelConverter
+ * @memberOf engine.conversion
+ */
+
+
+function buildModelConverter() {
+ return new ModelConverterBuilder();
}
/**
@@ -42402,1760 +42599,539 @@ function getSchemaValidRanges(attribute, ranges, schema) {
*/
/**
- * The inline autoformatting engine. Allows to format various inline patterns. For example,
- * it can be configured to make "foo" bold when typed `**foo**` (the `**` markers will be removed).
- *
- * The autoformatting operation is integrated with the undo manager,
- * so the autoformatting step can be undone, if the user's intention wasn't to format the text.
- *
- * See the constructors documentation to learn how to create custom inline autoformatters. You can also use
- * the {@link autoformat.Autoformat} feature which enables a set of default autoformatters (lists, headings, bold and italic).
+ * View matcher class.
+ * Instance of this class can be used to find {@link engine.view.Element elements} that match given pattern.
*
- * @memberOf autoformat
+ * @memberOf engine.view
*/
+var Matcher = function () {
+ /**
+ * Creates new instance of Matcher.
+ *
+ * @param {String|RegExp|Object} [pattern] Match patterns. See {@link engine.view.Matcher#add add method} for
+ * more information.
+ */
+ function Matcher() {
+ classCallCheck(this, Matcher);
-var InlineAutoformatEngine =
-/**
- * Enables autoformatting mechanism on a given {@link core.editor.Editor}.
- *
- * It formats the matched text by applying given model attribute or by running the provided formatting callback.
- * Each time data model changes text from given node (from the beginning of the current node to the collapsed
- * selection location) will be tested.
- *
- * @param {core.editor.Editor} editor Editor instance.
- * @param {Function|RegExp} testRegexpOrCallback RegExp or callback to execute on text.
- * Provided RegExp *must* have three capture groups. First and third capture groups
- * should match opening/closing delimiters. Second capture group should match text to format.
- *
- * // Matches `**bold text**` pattern.
- * // There are three capturing groups:
- * // - first to match starting `**` delimiter,
- * // - second to match text to format,
- * // - third to match ending `**` delimiter.
- * new InlineAutoformatEngine( this.editor, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' );
- *
- * When function is provided instead of RegExp, it will be executed with text to match as a parameter. Function
- * should return proper "ranges" to delete and format.
- *
- * {
- * remove: [
- * [ 0, 1 ], // Remove first letter from the given text.
- * [ 5, 6 ] // Remove 6th letter from the given text.
- * ],
- * format: [
- * [ 1, 5 ] // Format all letters from 2nd to 5th.
- * ]
- * }
- *
- * @param {Function|String} attributeOrCallback Name of attribute to apply on matching text or callback for manual
- * formatting.
- *
- * // Use attribute name:
- * new InlineAutoformatEngine( this.editor, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' );
- *
- * // Use formatting callback:
- * new InlineAutoformatEngine( this.editor, /(\*\*)([^\*]+?)(\*\*)$/g, ( batch, validRanges ) => {
- * for ( let range of validRanges ) {
- * batch.setAttribute( range, command, true );
- * }
- * } );
- */
-function InlineAutoformatEngine(editor, testRegexpOrCallback, attributeOrCallback) {
- var _this = this;
-
- classCallCheck(this, InlineAutoformatEngine);
-
- this.editor = editor;
-
- var regExp = void 0;
- var command = void 0;
- var testCallback = void 0;
- var formatCallback = void 0;
-
- if (testRegexpOrCallback instanceof RegExp) {
- regExp = testRegexpOrCallback;
- } else {
- testCallback = testRegexpOrCallback;
- }
+ this._patterns = [];
- if (typeof attributeOrCallback == 'string') {
- command = attributeOrCallback;
- } else {
- formatCallback = attributeOrCallback;
+ this.add.apply(this, arguments);
}
- // A test callback run on changed text.
- testCallback = testCallback || function (text) {
- var result = void 0;
- var remove = [];
- var format = [];
-
- while ((result = regExp.exec(text)) !== null) {
- // There should be full match and 3 capture groups.
- if (result && result.length < 4) {
- break;
- }
-
- var _result = result;
- var index = _result.index;
- var leftDel = _result['1'];
- var content = _result['2'];
- var rightDel = _result['3'];
-
- // Real matched string - there might be some non-capturing groups so we need to recalculate starting index.
-
- var found = leftDel + content + rightDel;
- index += result[0].length - found.length;
-
- // Start and End offsets of delimiters to remove.
- var delStart = [index, index + leftDel.length];
- var delEnd = [index + leftDel.length + content.length, index + leftDel.length + content.length + rightDel.length];
-
- remove.push(delStart);
- remove.push(delEnd);
-
- format.push([index + leftDel.length, index + leftDel.length + content.length]);
- }
-
- return {
- remove: remove,
- format: format
- };
- };
-
- // A format callback run on matched text.
- formatCallback = formatCallback || function (batch, validRanges) {
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
-
- try {
- for (var _iterator = validRanges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var range = _step.value;
-
- batch.setAttribute(range, command, true);
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
- };
-
- editor.document.on('change', function (evt, type) {
- if (type !== 'insert') {
- return;
- }
-
- var selection = _this.editor.document.selection;
-
- if (!selection.isCollapsed || !selection.focus || !selection.focus.parent) {
- return;
- }
-
- var block = selection.focus.parent;
- var text = getText(block).slice(0, selection.focus.offset + 1);
- var ranges = testCallback(text);
- var rangesToFormat = [];
-
- // Apply format before deleting text.
- ranges.format.forEach(function (range) {
- if (range[0] === undefined || range[1] === undefined) {
- return;
- }
-
- rangesToFormat.push(LiveRange.createFromParentsAndOffsets(block, range[0], block, range[1]));
- });
-
- var rangesToRemove = [];
-
- // Reverse order to not mix the offsets while removing.
- ranges.remove.slice().reverse().forEach(function (range) {
- if (range[0] === undefined || range[1] === undefined) {
- return;
- }
-
- rangesToRemove.push(LiveRange.createFromParentsAndOffsets(block, range[0], block, range[1]));
- });
-
- if (!(rangesToFormat.length && rangesToRemove.length)) {
- return;
- }
-
- var batch = editor.document.batch();
-
- editor.document.enqueueChanges(function () {
- var validRanges = getSchemaValidRanges(command, rangesToFormat, editor.document.schema);
-
- // Apply format.
- formatCallback(batch, validRanges);
-
- // Remove delimiters.
- var _iteratorNormalCompletion2 = true;
- var _didIteratorError2 = false;
- var _iteratorError2 = undefined;
+ /**
+ * Adds pattern or patterns to matcher instance.
+ *
+ * Example patterns matching element's name:
+ *
+ * // String.
+ * matcher.add( 'div' );
+ * matcher.add( { name: 'div' } );
+ *
+ * // Regular expression.
+ * matcher.add( /^\w/ );
+ * matcher.add( { name: /^\w/ } );
+ *
+ * Example pattern matching element's attributes:
+ *
+ * matcher.add( {
+ * attributes: {
+ * title: 'foobar',
+ * foo: /^\w+/
+ * }
+ * } );
+ *
+ * Example patterns matching element's classes:
+ *
+ * // Single class.
+ * matcher.add( {
+ * class: 'foobar'
+ * } );
+ *
+ * // Single class using regular expression.
+ * matcher.add( {
+ * class: /foo.../
+ * } );
+ *
+ * // Multiple classes to match.
+ * matcher.add( {
+ * class: [ 'baz', 'bar', /foo.../ ]
+ * } ):
+ *
+ * Example pattern matching element's styles:
+ *
+ * matcher.add( {
+ * style: {
+ * position: 'absolute',
+ * color: /^\w*blue$/
+ * }
+ * } );
+ *
+ * Example function pattern:
+ *
+ * matcher.add( ( element ) => {
+ * // Result of this function will be included in `match`
+ * // property of the object returned from matcher.match() call.
+ * if ( element.name === 'div' && element.childCount > 0 ) {
+ * return { name: true };
+ * }
+ *
+ * return null;
+ * } );
+ *
+ * Multiple patterns can be added in one call:
+ *
+ * matcher.add( 'div', { class: 'foobar' } );
+ *
+ * @param {Object|String|RegExp|function} pattern Object describing pattern details. If string or regular expression
+ * is provided it will be used to match element's name. Pattern can be also provided in a form
+ * of a function - then this function will be called with each {@link engine.view.Element element} as a parameter.
+ * Function's return value will be stored under `match` key of the object returned from
+ * {@link engine.view.Matcher#match match} or {@link engine.view.Matcher#matchAll matchAll} methods.
+ * @param {String|RegExp} [pattern.name] Name or regular expression to match element's name.
+ * @param {Object} [pattern.attribute] Object with key-value pairs representing attributes to match. Each object key
+ * represents attribute name. Value under that key can be either a string or a regular expression and it will be
+ * used to match attribute value.
+ * @param {String|RegExp|Array} [pattern.class] Class name or array of class names to match. Each name can be
+ * provided in a form of string or regular expression.
+ * @param {Object} [pattern.style] Object with key-value pairs representing styles to match. Each object key
+ * represents style name. Value under that key can be either a string or a regular expression and it will be used
+ * to match style value.
+ */
- try {
- for (var _iterator2 = rangesToRemove[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- var range = _step2.value;
- batch.remove(range);
- }
- } catch (err) {
- _didIteratorError2 = true;
- _iteratorError2 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion2 && _iterator2.return) {
- _iterator2.return();
- }
- } finally {
- if (_didIteratorError2) {
- throw _iteratorError2;
- }
- }
+ createClass(Matcher, [{
+ key: 'add',
+ value: function add() {
+ for (var _len = arguments.length, pattern = Array(_len), _key = 0; _key < _len; _key++) {
+ pattern[_key] = arguments[_key];
}
- });
- });
-};
-
-function getText(element) {
- return Array.from(element.getChildren()).reduce(function (a, b) {
- return a + b.data;
- }, '');
-}
-
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * The base class for CKEditor feature classes. Features are main way to enhance CKEditor abilities with tools,
- * utilities, services and components.
- *
- * The main responsibilities for Feature are:
- * * setting required dependencies (see {@link core.Plugin#requires},
- * * configuring, instantiating and registering commands to editor,
- * * registering converters to editor (if the feature operates on Tree Model),
- * * setting and registering UI components (if the feature uses it).
- *
- * @memberOf core
- */
-
-var Feature = function (_Plugin) {
- inherits(Feature, _Plugin);
-
- function Feature() {
- classCallCheck(this, Feature);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Feature).apply(this, arguments));
- }
-
- return Feature;
-}(Plugin);
-
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * Provides chainable, high-level API to easily build basic model-to-view converters that are appended to given
- * dispatchers. In many cases, this is the API that should be used to specify how abstract model elements and
- * attributes should be represented in the view (and then later in DOM). Instances of this class are created by
- * {@link engine.conversion.buildModelConverter}.
- *
- * If you need more complex converters, see {@link engine.conversion.ModelConversionDispatcher},
- * {@link engine.conversion.modelToView}, {@link engine.conversion.ModelConsumable}, {@link engine.conversion.Mapper}.
- *
- * Using this API it is possible to create three kinds of converters:
- *
- * 1. Model element to view element converter. This is a converter that takes the model element and represents it
- * in the view.
- *
- * buildModelConverter().for( dispatcher ).fromElement( 'paragraph' ).toElement( 'p' );
- * buildModelConverter().for( dispatcher ).fromElement( 'image' ).toElement( 'img' );
- *
- * 2. Model attribute to view attribute converter. This is a converter that operates on model element attributes
- * and converts them to view element attributes. It is suitable for elements like `image` (`src`, `title` attributes).
- *
- * buildModelConverter().for( dispatcher ).fromElement( 'image' ).toElement( 'img' );
- * buildModelConverter().for( dispatcher ).fromAttribute( 'src' ).toAttribute();
- *
- * 3. Model attribute to view element converter. This is a converter that takes model attributes and represents them
- * as view elements. Elements created by this kind of converter are wrapping other view elements. Wrapped view nodes
- * correspond to model nodes had converter attribute. It is suitable for attributes like `bold`, where `bold` attribute
- * set on model text nodes is converter to `strong` view element.
- *
- * buildModelConverter().for( dispatcher ).fromAttribute( 'bold' ).toElement( 'strong' );
- *
- * It is possible to provide various different parameters for {@link engine.conversion.ModelConverterBuilder#toElement}
- * and {@link engine.conversion.ModelConverterBuilder#toAttribute} methods. See their descriptions to learn more.
- *
- * It is also possible to {@link engine.conversion.ModelConverterBuilder#withPriority change default priority}
- * of created converters to decide which converter should be fired earlier and which later. This is useful if you have
- * a general converter but also want to provide different special-case converters (i.e. given model element is converted
- * always to given view element, but if it has given attribute it is converter to other view element). For this,
- * use {@link engine.conversion.ModelConverterBuilder#withPriority withPriority} right after `from...` method.
- *
- * Note that `to...` methods are "terminators", which means that should be the last one used in building converter.
- *
- * You can use {@link engine.conversion.ViewConverterBuilder} to create "opposite" converters - from view to model.
- *
- * @memberOf engine.conversion
- */
-
-var ModelConverterBuilder = function () {
- /**
- * Creates `ModelConverterBuilder` with given `dispatchers` registered to it.
- */
- function ModelConverterBuilder() {
- classCallCheck(this, ModelConverterBuilder);
-
- /**
- * Dispatchers to which converters will be attached.
- *
- * @type {Array.}
- * @private
- */
- this._dispatchers = [];
-
- /**
- * Contains data about registered "from" query.
- *
- * @type {Object}
- * @private
- */
- this._from = null;
- }
-
- /**
- * Set one or more dispatchers which the built converter will be attached to.
- *
- * @chainable
- * @param {...engine.conversion.ModelConversionDispatcher} dispatchers One or more dispatchers.
- * @returns {engine.conversion.ModelConverterBuilder}
- */
-
-
- createClass(ModelConverterBuilder, [{
- key: 'for',
- value: function _for() {
- for (var _len = arguments.length, dispatchers = Array(_len), _key = 0; _key < _len; _key++) {
- dispatchers[_key] = arguments[_key];
- }
-
- this._dispatchers = dispatchers;
-
- return this;
- }
-
- /**
- * Registers what model element should be converted.
- *
- * @chainable
- * @param {String} elementName Name of element to convert.
- * @returns {engine.conversion.ModelConverterBuilder}
- */
-
- }, {
- key: 'fromElement',
- value: function fromElement(elementName) {
- this._from = {
- type: 'element',
- name: elementName,
- priority: null
- };
-
- return this;
- }
-
- /**
- * Registers what model attribute should be converted.
- *
- * @chainable
- * @param {String} key Key of attribute to convert.
- * @returns {engine.conversion.ModelConverterBuilder}
- */
-
- }, {
- key: 'fromAttribute',
- value: function fromAttribute(key) {
- this._from = {
- type: 'attribute',
- key: key,
- priority: null
- };
-
- return this;
- }
-
- /**
- * Changes default priority for built converter. The lower the number, the earlier converter will be fired.
- * Default priority is `10`.
- *
- * **Note:** Keep in mind that event priority, that is set by this modifier, is used for attribute priority
- * when {@link engine.view.writer} is used. This changes how view elements are ordered,
- * i.e.: `foo ` vs `foo `. Using priority you can also
- * prevent node merging, i.e.: `foo ` vs `foo `.
- * If you want to prevent merging, just set different priority for both converters.
- *
- * buildModelConverter().for( dispatcher ).fromAttribute( 'bold' ).withPriority( 2 ).toElement( 'strong' );
- * buildModelConverter().for( dispatcher ).fromAttribute( 'italic' ).withPriority( 3 ).toElement( 'em' );
- *
- * @chainable
- * @param {Number} priority Converter priority.
- * @returns {engine.conversion.ModelConverterBuilder}
- */
-
- }, {
- key: 'withPriority',
- value: function withPriority(priority) {
- this._from.priority = priority;
-
- return this;
- }
-
- /**
- * Registers what view element will be created by converter.
- *
- * Method accepts various ways of providing how the view element will be created. You can pass view element name as
- * `string`, view element instance which will be cloned and used, or creator function which returns view element that
- * will be used. Keep in mind that when you view element instance or creator function, it has to be/return a
- * proper type of view element: {@link engine.view.ViewContainerElement ViewContainerElement} if you convert
- * from element or {@link engine.view.ViewAttributeElement ViewAttributeElement} if you convert from attribute.
- *
- * buildModelConverter().for( dispatcher ).fromElement( 'paragraph' ).toElement( 'p' );
- *
- * buildModelConverter().for( dispatcher ).fromElement( 'image' ).toElement( new ViewContainerElement( 'img' ) );
- *
- * buildModelConverter().for( dispatcher )
- * .fromElement( 'header' )
- * .toElement( ( data ) => new ViewContainerElement( 'h' + data.item.getAttribute( 'level' ) ) );
- *
- * buildModelConverter().for( dispatcher ).fromAttribute( 'bold' ).toElement( new ViewAttributeElement( 'strong' ) );
- *
- * Creator function will be passed different values depending whether conversion is from element or from attribute:
- *
- * * from element: dispatcher's {@link engine.conversion.ModelConversionDispatcher#event:insert insert event} parameters
- * will be passed,
- * * from attribute: there is one parameter and it is attribute value.
- *
- * This method also registers model selection to view selection converter, if conversion is from attribute.
- *
- * This method creates the converter and adds it as a callback to a proper
- * {@link engine.conversion.ModelConversionDispatcher conversion dispatcher} event.
- *
- * @param {String|engine.view.ViewElement|Function} element Element created by converter.
- */
-
- }, {
- key: 'toElement',
- value: function toElement(element) {
- var priority = this._from.priority === null ? 'normal' : this._from.priority;
-
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
-
- try {
- for (var _iterator = this._dispatchers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var dispatcher = _step.value;
-
- if (this._from.type == 'element') {
- // From model element to view element -> insert element.
- element = typeof element == 'string' ? new ContainerElement(element) : element;
-
- dispatcher.on('insert:' + this._from.name, insertElement(element), { priority: priority });
- } else {
- // From model attribute to view element -> wrap and unwrap.
- element = typeof element == 'string' ? new AttributeElement(element) : element;
-
- dispatcher.on('addAttribute:' + this._from.key, wrap(element), { priority: priority });
- dispatcher.on('changeAttribute:' + this._from.key, wrap(element), { priority: priority });
- dispatcher.on('removeAttribute:' + this._from.key, unwrap(element), { priority: priority });
-
- dispatcher.on('selectionAttribute:' + this._from.key, convertSelectionAttribute(element), { priority: priority });
- }
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
- }
-
- /**
- * Registers what view attribute will be created by converter. Keep in mind, that only model attribute to
- * view attribute conversion is supported.
- *
- * Method accepts various ways of providing how the view attribute will be created:
- *
- * * for no passed parameter, attribute key and value will be converted 1-to-1 to view attribute,
- * * if you pass one `string`, it will be used as new attribute key while attribute value will be copied,
- * * if you pass two `string`s, first one will be used as new attribute key and second one as new attribute value,
- * * if you pass a function, it is expected to return an object with `key` and `value` properties representing attribute key and value.
- * This function will be passed model attribute value and model attribute key as first two parameters and then
- * all dispatcher's {engine.conversion.ModelConversionDispatcher#event:changeAttribute changeAttribute event} parameters.
- *
- * buildModelConverter().for( dispatcher ).fromAttribute( 'class' ).toAttribute( '' );
- *
- * buildModelConverter().for( dispatcher ).fromAttribute( 'linkTitle' ).toAttribute( 'title' );
- *
- * buildModelConverter().for( dispatcher ).fromAttribute( 'highlighted' ).toAttribute( 'style', 'background:yellow' );
- *
- * buildModelConverter().for( dispatcher )
- * .fromAttribute( 'theme' )
- * .toAttribute( ( value ) => ( { key: 'class', value: value + '-theme' } ) );
- *
- * This method creates the converter and adds it as a callback to a proper
- * {@link engine.conversion.ModelConversionDispatcher conversion dispatcher} event.
- *
- * @param {String|Function} [keyOrCreator] Attribute key or a creator function.
- * @param {*} [value] Attribute value.
- */
-
- }, {
- key: 'toAttribute',
- value: function toAttribute(keyOrCreator, value) {
- if (this._from.type == 'element') {
- // Converting from model element to view attribute is unsupported.
- return;
- }
-
- var attributeCreator = void 0;
-
- if (!keyOrCreator) {
- // If `keyOrCreator` is not set, we assume default behavior which is 1:1 attribute re-write.
- // This is also a default behavior for `setAttribute` converter when no attribute creator is passed.
- attributeCreator = undefined;
- } else if (typeof keyOrCreator == 'string') {
- // `keyOrCreator` is an attribute key.
-
- if (value) {
- // If value is set, create "dumb" creator that always returns the same object.
- attributeCreator = function attributeCreator() {
- return { key: keyOrCreator, value: value };
- };
- } else {
- // If value is not set, take it from the passed parameter.
- attributeCreator = function attributeCreator(value) {
- return { key: keyOrCreator, value: value };
- };
- }
- } else {
- // `keyOrCreator` is an attribute creator function.
- attributeCreator = keyOrCreator;
- }
-
- var _iteratorNormalCompletion2 = true;
- var _didIteratorError2 = false;
- var _iteratorError2 = undefined;
-
- try {
- for (var _iterator2 = this._dispatchers[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- var dispatcher = _step2.value;
-
- var options = { priority: this._from.priority || 'normal' };
-
- dispatcher.on('addAttribute:' + this._from.key, setAttribute(attributeCreator), options);
- dispatcher.on('changeAttribute:' + this._from.key, setAttribute(attributeCreator), options);
- dispatcher.on('removeAttribute:' + this._from.key, removeAttribute(attributeCreator), options);
- }
- } catch (err) {
- _didIteratorError2 = true;
- _iteratorError2 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion2 && _iterator2.return) {
- _iterator2.return();
- }
- } finally {
- if (_didIteratorError2) {
- throw _iteratorError2;
- }
- }
- }
- }
- }]);
- return ModelConverterBuilder;
-}();
-
-/**
- * Entry point for model-to-view converters builder. This chainable API makes it easy to create basic, most common
- * model-to-view converters and attach them to provided dispatchers. The method returns an instance of
- * {@link engine.conversion.ModelConverterBuilder}.
- *
- * @external engine.conversion.buildModelConverter
- * @memberOf engine.conversion
- */
-
-
-function buildModelConverter() {
- return new ModelConverterBuilder();
-}
-
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * View matcher class.
- * Instance of this class can be used to find {@link engine.view.Element elements} that match given pattern.
- *
- * @memberOf engine.view
- */
-var Matcher = function () {
- /**
- * Creates new instance of Matcher.
- *
- * @param {String|RegExp|Object} [pattern] Match patterns. See {@link engine.view.Matcher#add add method} for
- * more information.
- */
- function Matcher() {
- classCallCheck(this, Matcher);
-
- this._patterns = [];
-
- this.add.apply(this, arguments);
- }
-
- /**
- * Adds pattern or patterns to matcher instance.
- *
- * Example patterns matching element's name:
- *
- * // String.
- * matcher.add( 'div' );
- * matcher.add( { name: 'div' } );
- *
- * // Regular expression.
- * matcher.add( /^\w/ );
- * matcher.add( { name: /^\w/ } );
- *
- * Example pattern matching element's attributes:
- *
- * matcher.add( {
- * attributes: {
- * title: 'foobar',
- * foo: /^\w+/
- * }
- * } );
- *
- * Example patterns matching element's classes:
- *
- * // Single class.
- * matcher.add( {
- * class: 'foobar'
- * } );
- *
- * // Single class using regular expression.
- * matcher.add( {
- * class: /foo.../
- * } );
- *
- * // Multiple classes to match.
- * matcher.add( {
- * class: [ 'baz', 'bar', /foo.../ ]
- * } ):
- *
- * Example pattern matching element's styles:
- *
- * matcher.add( {
- * style: {
- * position: 'absolute',
- * color: /^\w*blue$/
- * }
- * } );
- *
- * Example function pattern:
- *
- * matcher.add( ( element ) => {
- * // Result of this function will be included in `match`
- * // property of the object returned from matcher.match() call.
- * if ( element.name === 'div' && element.childCount > 0 ) {
- * return { name: true };
- * }
- *
- * return null;
- * } );
- *
- * Multiple patterns can be added in one call:
- *
- * matcher.add( 'div', { class: 'foobar' } );
- *
- * @param {Object|String|RegExp|function} pattern Object describing pattern details. If string or regular expression
- * is provided it will be used to match element's name. Pattern can be also provided in a form
- * of a function - then this function will be called with each {@link engine.view.Element element} as a parameter.
- * Function's return value will be stored under `match` key of the object returned from
- * {@link engine.view.Matcher#match match} or {@link engine.view.Matcher#matchAll matchAll} methods.
- * @param {String|RegExp} [pattern.name] Name or regular expression to match element's name.
- * @param {Object} [pattern.attribute] Object with key-value pairs representing attributes to match. Each object key
- * represents attribute name. Value under that key can be either a string or a regular expression and it will be
- * used to match attribute value.
- * @param {String|RegExp|Array} [pattern.class] Class name or array of class names to match. Each name can be
- * provided in a form of string or regular expression.
- * @param {Object} [pattern.style] Object with key-value pairs representing styles to match. Each object key
- * represents style name. Value under that key can be either a string or a regular expression and it will be used
- * to match style value.
- */
-
-
- createClass(Matcher, [{
- key: 'add',
- value: function add() {
- for (var _len = arguments.length, pattern = Array(_len), _key = 0; _key < _len; _key++) {
- pattern[_key] = arguments[_key];
- }
-
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
-
- try {
- for (var _iterator = pattern[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var item = _step.value;
-
- // String or RegExp pattern is used as element's name.
- if (typeof item == 'string' || item instanceof RegExp) {
- item = { name: item };
- }
-
- // Single class name/RegExp can be provided.
- if (item.class && (typeof item.class == 'string' || item.class instanceof RegExp)) {
- item.class = [item.class];
- }
-
- this._patterns.push(item);
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
- }
-
- /**
- * Matches elements for currently stored patterns. Returns match information about first found
- * {@link engine.view.Element element}, otherwise returns `null`.
- *
- * Example of returned object:
- *
- * {
- * element: ,
- * pattern: ,
- * match: {
- * name: true,
- * attributes: [ 'title', 'href' ],
- * classes: [ 'foo' ],
- * styles: [ 'color', 'position' ]
- * }
- * }
- *
- * @see engine.view.Matcher#add
- * @see engine.view.Matcher#matchAll
- * @param {...core.view.Element} element View element to match against stored patterns.
- * @returns {Object|null} result
- * @returns {core.view.Element} result.element Matched view element.
- * @returns {Object|String|RegExp|function} result.pattern Pattern that was used to find matched element.
- * @returns {Object} result.match Object representing matched element parts.
- * @returns {Boolean} [result.match.name] True if name of the element was matched.
- * @returns {Array} [result.match.attribute] Array with matched attribute names.
- * @returns {Array} [result.match.class] Array with matched class names.
- * @returns {Array} [result.match.style] Array with matched style names.
- */
-
- }, {
- key: 'match',
- value: function match() {
- for (var _len2 = arguments.length, element = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
- element[_key2] = arguments[_key2];
- }
-
- var _iteratorNormalCompletion2 = true;
- var _didIteratorError2 = false;
- var _iteratorError2 = undefined;
-
- try {
- for (var _iterator2 = element[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- var singleElement = _step2.value;
- var _iteratorNormalCompletion3 = true;
- var _didIteratorError3 = false;
- var _iteratorError3 = undefined;
-
- try {
- for (var _iterator3 = this._patterns[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
- var _pattern = _step3.value;
-
- var match = isElementMatching(singleElement, _pattern);
-
- if (match) {
- return {
- element: singleElement,
- pattern: _pattern,
- match: match
- };
- }
- }
- } catch (err) {
- _didIteratorError3 = true;
- _iteratorError3 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion3 && _iterator3.return) {
- _iterator3.return();
- }
- } finally {
- if (_didIteratorError3) {
- throw _iteratorError3;
- }
- }
- }
- }
- } catch (err) {
- _didIteratorError2 = true;
- _iteratorError2 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion2 && _iterator2.return) {
- _iterator2.return();
- }
- } finally {
- if (_didIteratorError2) {
- throw _iteratorError2;
- }
- }
- }
-
- return null;
- }
-
- /**
- * Matches elements for currently stored patterns. Returns array of match information with all found
- * {@link engine.view.Element elements}. If no element is found - returns `null`.
- *
- * @see engine.view.Matcher#add
- * @see engine.view.Matcher#match
- * @param {...engine.view.Element} element View element to match against stored patterns.
- * @returns {Array.|null} Array with match information about found elements or `null`. For more information
- * see {@link engine.view.Matcher#match match method} description.
- */
-
- }, {
- key: 'matchAll',
- value: function matchAll() {
- var results = [];
-
- for (var _len3 = arguments.length, element = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
- element[_key3] = arguments[_key3];
- }
-
- var _iteratorNormalCompletion4 = true;
- var _didIteratorError4 = false;
- var _iteratorError4 = undefined;
-
- try {
- for (var _iterator4 = element[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
- var singleElement = _step4.value;
- var _iteratorNormalCompletion5 = true;
- var _didIteratorError5 = false;
- var _iteratorError5 = undefined;
-
- try {
- for (var _iterator5 = this._patterns[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
- var _pattern2 = _step5.value;
-
- var match = isElementMatching(singleElement, _pattern2);
-
- if (match) {
- results.push({
- element: singleElement,
- pattern: _pattern2,
- match: match
- });
- }
- }
- } catch (err) {
- _didIteratorError5 = true;
- _iteratorError5 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion5 && _iterator5.return) {
- _iterator5.return();
- }
- } finally {
- if (_didIteratorError5) {
- throw _iteratorError5;
- }
- }
- }
- }
- } catch (err) {
- _didIteratorError4 = true;
- _iteratorError4 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion4 && _iterator4.return) {
- _iterator4.return();
- }
- } finally {
- if (_didIteratorError4) {
- throw _iteratorError4;
- }
- }
- }
-
- return results.length > 0 ? results : null;
- }
-
- /**
- * Returns the name of the element to match if there is exactly one pattern added to the matcher instance
- * and it matches element name defined by `string` (not `RegExp`). Otherwise, returns `null`.
- *
- * @returns {String|null} Element name trying to match.
- */
-
- }, {
- key: 'getElementName',
- value: function getElementName() {
- return this._patterns.length == 1 && this._patterns[0].name && !(this._patterns[0].name instanceof RegExp) ? this._patterns[0].name : null;
- }
- }]);
- return Matcher;
-}();
-
-function isElementMatching(element, pattern) {
- // If pattern is provided as function - return result of that function;
- if (typeof pattern == 'function') {
- return pattern(element);
- }
-
- var match = {};
- // Check element's name.
- if (pattern.name) {
- match.name = matchName(pattern.name, element.name);
-
- if (!match.name) {
- return null;
- }
- }
-
- // Check element's attributes.
- if (pattern.attribute) {
- match.attribute = matchAttributes(pattern.attribute, element);
-
- if (!match.attribute) {
- return null;
- }
- }
-
- // Check element's classes.
- if (pattern.class) {
- match.class = matchClasses(pattern.class, element);
-
- if (!match.class) {
- return false;
- }
- }
-
- // Check element's styles.
- if (pattern.style) {
- match.style = matchStyles(pattern.style, element);
-
- if (!match.style) {
- return false;
- }
- }
-
- return match;
-}
-
-// Checks if name can be matched by provided pattern.
-//
-// @param {String|RegExp} pattern
-// @param {String} name
-// @returns {Boolean} Returns `true` if name can be matched, `false` otherwise.
-function matchName(pattern, name) {
- // If pattern is provided as RegExp - test against this regexp.
- if (pattern instanceof RegExp) {
- return pattern.test(name);
- }
-
- return pattern === name;
-}
-
-// Checks if attributes of provided element can be matched against provided patterns.
-//
-// @param {Object} patterns Object with information about attributes to match. Each key of the object will be
-// used as attribute name. Value of each key can be a string or regular expression to match against attribute value.
-// @param {engine.view.Element} element Element which attributes will be tested.
-// @returns {Array|null} Returns array with matched attribute names or `null` if no attributes were matched.
-function matchAttributes(patterns, element) {
- var match = [];
-
- for (var name in patterns) {
- var pattern = patterns[name];
-
- if (element.hasAttribute(name)) {
- var attribute = element.getAttribute(name);
-
- if (pattern instanceof RegExp) {
- if (pattern.test(attribute)) {
- match.push(name);
- } else {
- return null;
- }
- } else if (attribute === pattern) {
- match.push(name);
- } else {
- return null;
- }
- } else {
- return null;
- }
- }
-
- return match;
-}
-
-// Checks if classes of provided element can be matched against provided patterns.
-//
-// @param {Array.} patterns Array of strings or regular expressions to match against element's classes.
-// @param {engine.view.Element} element Element which classes will be tested.
-// @returns {Array|null} Returns array with matched class names or `null` if no classes were matched.
-function matchClasses(patterns, element) {
- var match = [];
-
- var _iteratorNormalCompletion6 = true;
- var _didIteratorError6 = false;
- var _iteratorError6 = undefined;
-
- try {
- for (var _iterator6 = patterns[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
- var pattern = _step6.value;
-
- if (pattern instanceof RegExp) {
- var classes = element.getClassNames();
-
- var _iteratorNormalCompletion7 = true;
- var _didIteratorError7 = false;
- var _iteratorError7 = undefined;
-
- try {
- for (var _iterator7 = classes[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
- var name = _step7.value;
-
- if (pattern.test(name)) {
- match.push(name);
- }
- }
- } catch (err) {
- _didIteratorError7 = true;
- _iteratorError7 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion7 && _iterator7.return) {
- _iterator7.return();
- }
- } finally {
- if (_didIteratorError7) {
- throw _iteratorError7;
- }
- }
- }
-
- if (match.length === 0) {
- return null;
- }
- } else if (element.hasClass(pattern)) {
- match.push(pattern);
- } else {
- return null;
- }
- }
- } catch (err) {
- _didIteratorError6 = true;
- _iteratorError6 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion6 && _iterator6.return) {
- _iterator6.return();
- }
- } finally {
- if (_didIteratorError6) {
- throw _iteratorError6;
- }
- }
- }
-
- return match;
-}
-
-// Checks if styles of provided element can be matched against provided patterns.
-//
-// @param {Object} patterns Object with information about styles to match. Each key of the object will be
-// used as style name. Value of each key can be a string or regular expression to match against style value.
-// @param {engine.view.Element} element Element which styles will be tested.
-// @returns {Array|null} Returns array with matched style names or `null` if no styles were matched.
-function matchStyles(patterns, element) {
- var match = [];
-
- for (var name in patterns) {
- var pattern = patterns[name];
-
- if (element.hasStyle(name)) {
- var style = element.getStyle(name);
-
- if (pattern instanceof RegExp) {
- if (pattern.test(style)) {
- match.push(name);
- } else {
- return null;
- }
- } else if (style === pattern) {
- match.push(name);
- } else {
- return null;
- }
- } else {
- return null;
- }
- }
-
- return match;
-}
-
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * Provides chainable, high-level API to easily build basic view-to-model converters that are appended to given
- * dispatchers. View-to-model converters are used when external data is added to the editor, i.e. when a user pastes
- * HTML content to the editor. Then, converters are used to translate this structure, possibly removing unknown/incorrect
- * nodes, and add it to the model. Also multiple, different elements might be translated into the same thing in the
- * model, i.e. `` and `` elements might be converted to `bold` attribute (even though `bold` attribute will
- * be then converted only to `` tag). Instances of this class are created by {@link engine.conversion.buildViewConverter}.
- *
- * If you need more complex converters, see {@link engine.conversion.ViewConversionDispatcher},
- * {@link engine.conversion.viewToModel}, {@link engine.conversion.ViewConsumable}.
- *
- * Using this API it is possible to create various kind of converters:
- *
- * 1. View element to model element:
- *
- * buildViewConverter().for( dispatcher ).fromElement( 'p' ).toElement( 'paragraph' );
- *
- * 2. View element to model attribute:
- *
- * buildViewConverter().for( dispatcher ).fromElement( 'b' ).fromElement( 'strong' ).toAttribute( 'bold', 'true' );
- *
- * 3. View attribute to model attribute:
- *
- * buildViewConverter().for( dispatcher ).fromAttribute( 'style', { 'font-weight': 'bold' } ).toAttribute( 'bold', 'true' );
- * buildViewConverter().for( dispatcher )
- * .fromAttribute( 'class' )
- * .toAttribute( ( viewElement ) => ( { class: viewElement.getAttribute( 'class' ) } ) );
- *
- * 4. View elements and attributes to model attribute:
- *
- * buildViewConverter().for( dispatcher )
- * .fromElement( 'b' ).fromElement( 'strong' ).fromAttribute( 'style', { 'font-weight': 'bold' } )
- * .toAttribute( 'bold', 'true' );
- *
- * 5. View {@link engine.view.Matcher view element matcher instance} or {@link engine.view.Matcher#add matcher pattern}
- * to model element or attribute:
- *
- * const matcher = new ViewMatcher();
- * matcher.add( 'div', { class: 'quote' } );
- * buildViewConverter().for( dispatcher ).from( matcher ).toElement( 'quote' );
- *
- * buildViewConverter().for( dispatcher ).from( { name: 'span', class: 'bold' } ).toAttribute( 'bold', 'true' );
- *
- * Note, that converters built using `ViewConverterBuilder` automatically check {@link engine.model.Schema schema}
- * if created model structure is valid. If given conversion would be invalid according to schema, it is ignored.
- *
- * It is possible to provide creator functions as parameters for {@link engine.conversion.ViewConverterBuilder#toElement}
- * and {@link engine.conversion.ViewConverterBuilder#toAttribute} methods. See their descriptions to learn more.
- *
- * By default, converter will {@link engine.conversion.ViewConsumable#consume consume} every value specified in
- * given `from...` query, i.e. `.from( { name: 'span', class: 'bold' } )` will make converter consume both `span` name
- * and `bold` class. It is possible to change this behavior using {@link engine.conversion.ViewConverterBuilder#consuming consuming}
- * modifier. The modifier alters the last `fromXXX` query used before it. To learn more about consuming values,
- * see {@link engine.conversion.ViewConsumable}.
- *
- * It is also possible to {@link engine.conversion.ViewConverterBuilder#withPriority change default priority}
- * of created converters to decide which converter should be fired earlier and which later. This is useful if you provide
- * a general converter but want to provide different converter for a specific-case (i.e. given view element is converted
- * always to given model element, but if it has given class it is converter to other model element). For this,
- * use {@link engine.conversion.ViewConverterBuilder#withPriority withPriority} modifier. The modifier alters
- * the last `from...` query used before it.
- *
- * Note that `to...` methods are "terminators", which means that should be the last one used in building converter.
- *
- * You can use {@link engine.conversion.ModelConverterBuilder} to create "opposite" converters - from model to view.
- *
- * @memberOf engine.conversion
- */
-
-var ViewConverterBuilder = function () {
- /**
- * Creates `ViewConverterBuilder` with given `dispatchers` registered to it.
- */
- function ViewConverterBuilder() {
- classCallCheck(this, ViewConverterBuilder);
-
- /**
- * Dispatchers to which converters will be attached.
- *
- * @type {Array.}
- * @private
- */
- this._dispatchers = [];
-
- /**
- * Stores "from" queries.
- *
- * @type {Array}
- * @private
- */
- this._from = [];
- }
-
- /**
- * Set one or more dispatchers which the built converter will be attached to.
- *
- * @chainable
- * @param {...engine.conversion.ViewConversionDispatcher} dispatchers One or more dispatchers.
- * @returns {engine.conversion.ViewConverterBuilder}
- */
-
-
- createClass(ViewConverterBuilder, [{
- key: 'for',
- value: function _for() {
- for (var _len = arguments.length, dispatchers = Array(_len), _key = 0; _key < _len; _key++) {
- dispatchers[_key] = arguments[_key];
- }
-
- this._dispatchers = dispatchers;
-
- return this;
- }
-
- /**
- * Registers what view element should be converted.
- *
- * buildViewConverter().for( dispatcher ).fromElement( 'p' ).toElement( 'paragraph' );
- *
- * @chainable
- * @param {String} elementName View element name.
- * @returns {engine.conversion.ViewConverterBuilder}
- */
-
- }, {
- key: 'fromElement',
- value: function fromElement(elementName) {
- return this.from({ name: elementName });
- }
-
- /**
- * Registers what view attribute should be converted.
- *
- * buildViewConverter().for( dispatcher ).fromAttribute( 'style', { 'font-weight': 'bold' } ).toAttribute( 'bold', 'true' );
- *
- * @chainable
- * @param {String|RegExp} key View attribute key.
- * @param {String|RegExp} [value] View attribute value.
- * @returns {engine.conversion.ViewConverterBuilder}
- */
-
- }, {
- key: 'fromAttribute',
- value: function fromAttribute(key) {
- var value = arguments.length <= 1 || arguments[1] === undefined ? /.*/ : arguments[1];
-
- var pattern = {};
- pattern[key] = value;
-
- return this.from(pattern);
- }
-
- /**
- * Registers what view pattern should be converted. The method accepts either {@link engine.view.Matcher view matcher}
- * or view matcher pattern.
- *
- * const matcher = new ViewMatcher();
- * matcher.add( 'div', { class: 'quote' } );
- * buildViewConverter().for( dispatcher ).from( matcher ).toElement( 'quote' );
- *
- * buildViewConverter().for( dispatcher ).from( { name: 'span', class: 'bold' } ).toAttribute( 'bold', 'true' );
- *
- * @chainable
- * @param {Object|engine.view.Matcher} matcher View matcher or view matcher pattern.
- * @returns {engine.conversion.ViewConverterBuilder}
- */
-
- }, {
- key: 'from',
- value: function from(matcher) {
- if (!(matcher instanceof Matcher)) {
- matcher = new Matcher(matcher);
- }
-
- this._from.push({
- matcher: matcher,
- consume: false,
- priority: null
- });
-
- return this;
- }
-
- /**
- * Modifies which consumable values will be {@link engine.conversion.ViewConsumable#consume consumed} by built converter.
- * It modifies the last `from...` query. Can be used after each `from...` query in given chain. Useful for providing
- * more specific matches.
- *
- * // This converter will only handle class bold conversion (to proper attribute) but span element
- * // conversion will have to be done in separate converter.
- * // Without consuming modifier, the converter would consume both class and name, so a converter for
- * // span element would not be fired.
- * buildViewConverter().for( dispatcher )
- * .from( { name: 'span', class: 'bold' } ).consuming( { class: 'bold' } )
- * .toAttribute( 'bold', 'true' } );
- *
- * buildViewConverter().for( dispatcher )
- * .fromElement( 'img' ).consuming( { name: true, attributes: [ 'src', 'title' ] } )
- * .toElement( ( viewElement ) => new ModelElement( 'image', { src: viewElement.getAttribute( 'src' ),
- * title: viewElement.getAttribute( 'title' ) } );
- *
- * **Note:** All and only values from passed object has to be consumable on converted view element. This means that
- * using `consuming` method, you can either make looser conversion conditions (like in first example) or tighter
- * conversion conditions (like in second example). So, the view element, to be converter, has to match query of
- * `from...` method and then have to have enough consumable values to consume.
- *
- * @see engine.conversion.ViewConsumable
- * @chainable
- * @param {Object} consume Values to consume.
- * @returns {engine.conversion.ViewConverterBuilder}
- */
-
- }, {
- key: 'consuming',
- value: function consuming(consume) {
- var lastFrom = this._from[this._from.length - 1];
- lastFrom.consume = consume;
-
- return this;
- }
-
- /**
- * Changes default priority for built converter. It modifies the last `from...` query. Can be used after each
- * `from...` query in given chain. Useful for overwriting converters. The lower the number, the earlier converter will be fired.
- *
- * buildViewConverter().for( dispatcher ).fromElement( 'p' ).toElement( 'paragraph' );
- * // Register converter with proper priority, otherwise "p" element would get consumed by first
- * // converter and the second converter would not be fired.
- * buildViewConverter().for( dispatcher )
- * .from( { name: 'p', class: 'custom' } ).withPriority( 9 )
- * .toElement( 'customParagraph' );
- *
- * **Note:** `ViewConverterBuilder` takes care so all `toElement` conversions takes place before all `toAttribute`
- * conversions. This is done by setting default `toElement` priority to `10` and `toAttribute` priority to `1000`.
- * It is recommended to set converter priority for `toElement` conversions below `500` and `toAttribute` priority
- * above `500`. It is important that model elements are created before attributes, otherwise attributes would
- * not be applied or other errors may occur.
- *
- * @chainable
- * @param {Number} priority Converter priority.
- * @returns {engine.conversion.ViewConverterBuilder}
- */
-
- }, {
- key: 'withPriority',
- value: function withPriority(priority) {
- var lastFrom = this._from[this._from.length - 1];
- lastFrom.priority = priority;
-
- return this;
- }
-
- /**
- * Registers what model element will be created by converter.
- *
- * Method accepts two ways of providing what kind of model element will be created. You can pass model element
- * name as a `string` or a function that will return model element instance. If you provide creator function,
- * it will be passed converted view element as first and only parameter.
- *
- * buildViewConverter().for( dispatcher ).fromElement( 'p' ).toElement( 'paragraph' );
- * buildViewConverter().for( dispatcher )
- * .fromElement( 'img' )
- * .toElement( ( viewElement ) => new ModelElement( 'image', { src: viewElement.getAttribute( 'src' ) } );
- *
- * @param {String|Function} element Model element name or model element creator function.
- */
-
- }, {
- key: 'toElement',
- value: function toElement(element) {
- var eventCallbackGen = function eventCallbackGen(from) {
- return function (evt, data, consumable, conversionApi) {
- // There is one callback for all patterns in the matcher.
- // This will be usually just one pattern but we support matchers with many patterns too.
- var matchAll = from.matcher.matchAll(data.input);
-
- // If there is no match, this callback should not do anything.
- if (!matchAll) {
- return;
- }
-
- // Now, for every match between matcher and actual element, we will try to consume the match.
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
-
- try {
- for (var _iterator = matchAll[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var match = _step.value;
-
- // Create model element basing on creator function or element name.
- var modelElement = element instanceof Function ? element(data.input) : new Element(element);
-
- // Check whether generated structure is okay with `Schema`.
- var keys = Array.from(modelElement.getAttributeKeys());
-
- if (!conversionApi.schema.check({ name: modelElement.name, attributes: keys, inside: data.context })) {
- continue;
- }
-
- // Try to consume appropriate values from consumable values list.
- if (!consumable.consume(data.input, from.consume || match.match)) {
- continue;
- }
-
- // If everything is fine, we are ready to start the conversion.
- // Add newly created `modelElement` to the parents stack.
- data.context.push(modelElement);
- // Convert children of converted view element and append them to `modelElement`.
- var modelChildren = conversionApi.convertChildren(data.input, consumable, data);
- var insertPosition = Position.createAt(modelElement, 'end');
- modelWriter.insert(insertPosition, modelChildren);
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- // Remove created `modelElement` from the parents stack.
- data.context.pop();
+ try {
+ for (var _iterator = pattern[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var item = _step.value;
- // Add `modelElement` as a result.
- data.output = modelElement;
+ // String or RegExp pattern is used as element's name.
+ if (typeof item == 'string' || item instanceof RegExp) {
+ item = { name: item };
+ }
- // Prevent multiple conversion if there are other correct matches.
- break;
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
+ // Single class name/RegExp can be provided.
+ if (item.class && (typeof item.class == 'string' || item.class instanceof RegExp)) {
+ item.class = [item.class];
}
- };
- };
- this._setCallback(eventCallbackGen, 'normal');
+ this._patterns.push(item);
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
}
/**
- * Registers what model attribute will be created by converter.
+ * Matches elements for currently stored patterns. Returns match information about first found
+ * {@link engine.view.Element element}, otherwise returns `null`.
*
- * Method accepts two ways of providing what kind of model attribute will be created. You can either pass two strings
- * representing attribute key and attribute value or a function that returns an object with `key` and `value` properties.
- * If you provide creator function, it will be passed converted view element as first and only parameter.
+ * Example of returned object:
*
- * buildViewConverter().for( dispatcher ).fromAttribute( 'style', { 'font-weight': 'bold' } ).toAttribute( 'bold', 'true' );
- * buildViewConverter().for( dispatcher )
- * .fromAttribute( 'class' )
- * .toAttribute( ( viewElement ) => ( { key: 'class', value: viewElement.getAttribute( 'class' ) } ) );
+ * {
+ * element: ,
+ * pattern: ,
+ * match: {
+ * name: true,
+ * attributes: [ 'title', 'href' ],
+ * classes: [ 'foo' ],
+ * styles: [ 'color', 'position' ]
+ * }
+ * }
*
- * @param {String|Function} keyOrCreator Attribute key or a creator function.
- * @param {String} [value] Attribute value. Required if `keyOrCreator` is a `string`. Ignored otherwise.
+ * @see engine.view.Matcher#add
+ * @see engine.view.Matcher#matchAll
+ * @param {...core.view.Element} element View element to match against stored patterns.
+ * @returns {Object|null} result
+ * @returns {core.view.Element} result.element Matched view element.
+ * @returns {Object|String|RegExp|function} result.pattern Pattern that was used to find matched element.
+ * @returns {Object} result.match Object representing matched element parts.
+ * @returns {Boolean} [result.match.name] True if name of the element was matched.
+ * @returns {Array} [result.match.attribute] Array with matched attribute names.
+ * @returns {Array} [result.match.class] Array with matched class names.
+ * @returns {Array} [result.match.style] Array with matched style names.
*/
}, {
- key: 'toAttribute',
- value: function toAttribute(keyOrCreator, value) {
- var eventCallbackGen = function eventCallbackGen(from) {
- return function (evt, data, consumable, conversionApi) {
- // There is one callback for all patterns in the matcher.
- // This will be usually just one pattern but we support matchers with many patterns too.
- var matchAll = from.matcher.matchAll(data.input);
+ key: 'match',
+ value: function match() {
+ for (var _len2 = arguments.length, element = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+ element[_key2] = arguments[_key2];
+ }
- // If there is no match, this callback should not do anything.
- if (!matchAll) {
- return;
- }
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
- // Now, for every match between matcher and actual element, we will try to consume the match.
- var _iteratorNormalCompletion2 = true;
- var _didIteratorError2 = false;
- var _iteratorError2 = undefined;
+ try {
+ for (var _iterator2 = element[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var singleElement = _step2.value;
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
try {
- for (var _iterator2 = matchAll[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- var match = _step2.value;
+ for (var _iterator3 = this._patterns[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var _pattern = _step3.value;
- // Try to consume appropriate values from consumable values list.
- if (!consumable.consume(data.input, from.consume || match.match)) {
- continue;
- }
+ var match = isElementMatching(singleElement, _pattern);
- // Since we are converting to attribute we need an output on which we will set the attribute.
- // If the output is not created yet, we will create it.
- if (!data.output) {
- data.output = conversionApi.convertChildren(data.input, consumable, data);
+ if (match) {
+ return {
+ element: singleElement,
+ pattern: _pattern,
+ match: match
+ };
}
-
- // Use attribute creator function, if provided.
- var attribute = keyOrCreator instanceof Function ? keyOrCreator(data.input) : { key: keyOrCreator, value: value };
-
- // Set attribute on current `output`. `Schema` is checked inside this helper function.
- setAttributeOn(data.output, attribute, data, conversionApi);
-
- // Prevent multiple conversion if there are other correct matches.
- break;
}
} catch (err) {
- _didIteratorError2 = true;
- _iteratorError2 = err;
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
} finally {
try {
- if (!_iteratorNormalCompletion2 && _iterator2.return) {
- _iterator2.return();
+ if (!_iteratorNormalCompletion3 && _iterator3.return) {
+ _iterator3.return();
}
} finally {
- if (_didIteratorError2) {
- throw _iteratorError2;
+ if (_didIteratorError3) {
+ throw _iteratorError3;
}
}
}
- };
- };
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
- this._setCallback(eventCallbackGen, 'low');
+ return null;
}
/**
- * Helper function that uses given callback generator to created callback function and sets it on registered dispatchers.
+ * Matches elements for currently stored patterns. Returns array of match information with all found
+ * {@link engine.view.Element elements}. If no element is found - returns `null`.
*
- * @param eventCallbackGen
- * @param defaultPriority
- * @private
+ * @see engine.view.Matcher#add
+ * @see engine.view.Matcher#match
+ * @param {...engine.view.Element} element View element to match against stored patterns.
+ * @returns {Array.|null} Array with match information about found elements or `null`. For more information
+ * see {@link engine.view.Matcher#match match method} description.
*/
}, {
- key: '_setCallback',
- value: function _setCallback(eventCallbackGen, defaultPriority) {
- // We will add separate event callback for each registered `from` entry.
- var _iteratorNormalCompletion3 = true;
- var _didIteratorError3 = false;
- var _iteratorError3 = undefined;
-
- try {
- for (var _iterator3 = this._from[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
- var from = _step3.value;
+ key: 'matchAll',
+ value: function matchAll() {
+ var results = [];
- // We have to figure out event name basing on matcher's patterns.
- // If there is exactly one pattern and it has `name` property we will used that name.
- var matcherElementName = from.matcher.getElementName();
- var eventName = matcherElementName ? 'element:' + matcherElementName : 'element';
- var eventCallback = eventCallbackGen(from);
+ for (var _len3 = arguments.length, element = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
+ element[_key3] = arguments[_key3];
+ }
- var priority = from.priority === null ? defaultPriority : from.priority;
+ var _iteratorNormalCompletion4 = true;
+ var _didIteratorError4 = false;
+ var _iteratorError4 = undefined;
- // Add event to each registered dispatcher.
- var _iteratorNormalCompletion4 = true;
- var _didIteratorError4 = false;
- var _iteratorError4 = undefined;
+ try {
+ for (var _iterator4 = element[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
+ var singleElement = _step4.value;
+ var _iteratorNormalCompletion5 = true;
+ var _didIteratorError5 = false;
+ var _iteratorError5 = undefined;
try {
- for (var _iterator4 = this._dispatchers[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
- var dispatcher = _step4.value;
+ for (var _iterator5 = this._patterns[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
+ var _pattern2 = _step5.value;
- dispatcher.on(eventName, eventCallback, { priority: priority });
+ var match = isElementMatching(singleElement, _pattern2);
+
+ if (match) {
+ results.push({
+ element: singleElement,
+ pattern: _pattern2,
+ match: match
+ });
+ }
}
} catch (err) {
- _didIteratorError4 = true;
- _iteratorError4 = err;
+ _didIteratorError5 = true;
+ _iteratorError5 = err;
} finally {
try {
- if (!_iteratorNormalCompletion4 && _iterator4.return) {
- _iterator4.return();
+ if (!_iteratorNormalCompletion5 && _iterator5.return) {
+ _iterator5.return();
}
} finally {
- if (_didIteratorError4) {
- throw _iteratorError4;
+ if (_didIteratorError5) {
+ throw _iteratorError5;
}
}
}
}
} catch (err) {
- _didIteratorError3 = true;
- _iteratorError3 = err;
+ _didIteratorError4 = true;
+ _iteratorError4 = err;
} finally {
try {
- if (!_iteratorNormalCompletion3 && _iterator3.return) {
- _iterator3.return();
+ if (!_iteratorNormalCompletion4 && _iterator4.return) {
+ _iterator4.return();
}
} finally {
- if (_didIteratorError3) {
- throw _iteratorError3;
+ if (_didIteratorError4) {
+ throw _iteratorError4;
}
}
}
+
+ return results.length > 0 ? results : null;
+ }
+
+ /**
+ * Returns the name of the element to match if there is exactly one pattern added to the matcher instance
+ * and it matches element name defined by `string` (not `RegExp`). Otherwise, returns `null`.
+ *
+ * @returns {String|null} Element name trying to match.
+ */
+
+ }, {
+ key: 'getElementName',
+ value: function getElementName() {
+ return this._patterns.length == 1 && this._patterns[0].name && !(this._patterns[0].name instanceof RegExp) ? this._patterns[0].name : null;
}
}]);
- return ViewConverterBuilder;
+ return Matcher;
}();
-// Helper function that sets given attributes on given `engine.model.Node` or `engine.model.DocumentFragment`.
+function isElementMatching(element, pattern) {
+ // If pattern is provided as function - return result of that function;
+ if (typeof pattern == 'function') {
+ return pattern(element);
+ }
+ var match = {};
+ // Check element's name.
+ if (pattern.name) {
+ match.name = matchName(pattern.name, element.name);
-function setAttributeOn(toChange, attribute, data, conversionApi) {
- if (isIterable(toChange)) {
- var _iteratorNormalCompletion5 = true;
- var _didIteratorError5 = false;
- var _iteratorError5 = undefined;
+ if (!match.name) {
+ return null;
+ }
+ }
- try {
- for (var _iterator5 = toChange[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
- var node = _step5.value;
+ // Check element's attributes.
+ if (pattern.attribute) {
+ match.attribute = matchAttributes(pattern.attribute, element);
- setAttributeOn(node, attribute, data, conversionApi);
- }
- } catch (err) {
- _didIteratorError5 = true;
- _iteratorError5 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion5 && _iterator5.return) {
- _iterator5.return();
- }
- } finally {
- if (_didIteratorError5) {
- throw _iteratorError5;
- }
- }
+ if (!match.attribute) {
+ return null;
}
+ }
- return;
+ // Check element's classes.
+ if (pattern.class) {
+ match.class = matchClasses(pattern.class, element);
+
+ if (!match.class) {
+ return false;
+ }
}
- var keys = Array.from(toChange.getAttributeKeys());
- keys.push(attribute.key);
+ // Check element's styles.
+ if (pattern.style) {
+ match.style = matchStyles(pattern.style, element);
- var schemaQuery = {
- name: toChange.name || '$text',
- attributes: keys,
- inside: data.context
- };
+ if (!match.style) {
+ return false;
+ }
+ }
- if (conversionApi.schema.check(schemaQuery)) {
- toChange.setAttribute(attribute.key, attribute.value);
+ return match;
+}
+
+// Checks if name can be matched by provided pattern.
+//
+// @param {String|RegExp} pattern
+// @param {String} name
+// @returns {Boolean} Returns `true` if name can be matched, `false` otherwise.
+function matchName(pattern, name) {
+ // If pattern is provided as RegExp - test against this regexp.
+ if (pattern instanceof RegExp) {
+ return pattern.test(name);
}
+
+ return pattern === name;
}
-/**
- * Entry point for view-to-model converters builder. This chainable API makes it easy to create basic, most common
- * view-to-model converters and attach them to provided dispatchers. The method returns an instance of
- * {@link engine.conversion.ViewConverterBuilder}.
- *
- * @external engine.conversion.buildViewConverter
- * @memberOf engine.conversion
- */
-function buildViewConverter() {
- return new ViewConverterBuilder();
+// Checks if attributes of provided element can be matched against provided patterns.
+//
+// @param {Object} patterns Object with information about attributes to match. Each key of the object will be
+// used as attribute name. Value of each key can be a string or regular expression to match against attribute value.
+// @param {engine.view.Element} element Element which attributes will be tested.
+// @returns {Array|null} Returns array with matched attribute names or `null` if no attributes were matched.
+function matchAttributes(patterns, element) {
+ var match = [];
+
+ for (var name in patterns) {
+ var pattern = patterns[name];
+
+ if (element.hasAttribute(name)) {
+ var attribute = element.getAttribute(name);
+
+ if (pattern instanceof RegExp) {
+ if (pattern.test(attribute)) {
+ match.push(name);
+ } else {
+ return null;
+ }
+ } else if (attribute === pattern) {
+ match.push(name);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ return match;
}
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+// Checks if classes of provided element can be matched against provided patterns.
+//
+// @param {Array.} patterns Array of strings or regular expressions to match against element's classes.
+// @param {engine.view.Element} element Element which classes will be tested.
+// @returns {Array|null} Returns array with matched class names or `null` if no classes were matched.
+function matchClasses(patterns, element) {
+ var match = [];
-/**
- * The paragraph feature for the editor.
- * Introduces the `` element in the model which renders as a `` element in the DOM and data.
- *
- * @memberOf paragraph
- * @extends core.Feature
- */
+ var _iteratorNormalCompletion6 = true;
+ var _didIteratorError6 = false;
+ var _iteratorError6 = undefined;
-var Paragraph = function (_Feature) {
- inherits(Paragraph, _Feature);
+ try {
+ for (var _iterator6 = patterns[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
+ var pattern = _step6.value;
- function Paragraph() {
- classCallCheck(this, Paragraph);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Paragraph).apply(this, arguments));
+ if (pattern instanceof RegExp) {
+ var classes = element.getClassNames();
+
+ var _iteratorNormalCompletion7 = true;
+ var _didIteratorError7 = false;
+ var _iteratorError7 = undefined;
+
+ try {
+ for (var _iterator7 = classes[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
+ var name = _step7.value;
+
+ if (pattern.test(name)) {
+ match.push(name);
+ }
+ }
+ } catch (err) {
+ _didIteratorError7 = true;
+ _iteratorError7 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion7 && _iterator7.return) {
+ _iterator7.return();
+ }
+ } finally {
+ if (_didIteratorError7) {
+ throw _iteratorError7;
+ }
+ }
+ }
+
+ if (match.length === 0) {
+ return null;
+ }
+ } else if (element.hasClass(pattern)) {
+ match.push(pattern);
+ } else {
+ return null;
+ }
+ }
+ } catch (err) {
+ _didIteratorError6 = true;
+ _iteratorError6 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion6 && _iterator6.return) {
+ _iterator6.return();
+ }
+ } finally {
+ if (_didIteratorError6) {
+ throw _iteratorError6;
+ }
+ }
}
- createClass(Paragraph, [{
- key: 'init',
+ return match;
+}
- /**
- * @inheritDoc
- */
- value: function init() {
- var editor = this.editor;
- var data = editor.data;
- var editing = editor.editing;
+// Checks if styles of provided element can be matched against provided patterns.
+//
+// @param {Object} patterns Object with information about styles to match. Each key of the object will be
+// used as style name. Value of each key can be a string or regular expression to match against style value.
+// @param {engine.view.Element} element Element which styles will be tested.
+// @returns {Array|null} Returns array with matched style names or `null` if no styles were matched.
+function matchStyles(patterns, element) {
+ var match = [];
- // Schema.
- editor.document.schema.registerItem('paragraph', '$block');
+ for (var name in patterns) {
+ var pattern = patterns[name];
- // Build converter from model to view for data and editing pipelines.
- buildModelConverter().for(data.modelToView, editing.modelToView).fromElement('paragraph').toElement('p');
+ if (element.hasStyle(name)) {
+ var style = element.getStyle(name);
- // Build converter from view to model for data pipeline.
- buildViewConverter().for(data.viewToModel).fromElement('p').toElement('paragraph');
+ if (pattern instanceof RegExp) {
+ if (pattern.test(style)) {
+ match.push(name);
+ } else {
+ return null;
+ }
+ } else if (style === pattern) {
+ match.push(name);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
}
- }]);
- return Paragraph;
-}(Feature);
+ }
+
+ return match;
+}
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -44163,584 +43139,557 @@ var Paragraph = function (_Feature) {
*/
/**
- * The base class for CKEditor commands.
+ * Provides chainable, high-level API to easily build basic view-to-model converters that are appended to given
+ * dispatchers. View-to-model converters are used when external data is added to the editor, i.e. when a user pastes
+ * HTML content to the editor. Then, converters are used to translate this structure, possibly removing unknown/incorrect
+ * nodes, and add it to the model. Also multiple, different elements might be translated into the same thing in the
+ * model, i.e. `` and `` elements might be converted to `bold` attribute (even though `bold` attribute will
+ * be then converted only to `` tag). Instances of this class are created by {@link engine.conversion.buildViewConverter}.
*
- * Commands are main way to manipulate editor contents and state. They are mostly used by UI elements (or by other
- * commands) to make changes in Tree Model. Commands are available in every part of code that has access to
- * {@link core.editor.Editor} instance, since they are registered in it and executed through {@link core.editor.Editor#execute}.
- * Commands instances are available through {@link core.editor.Editor#commands}.
+ * If you need more complex converters, see {@link engine.conversion.ViewConversionDispatcher},
+ * {@link engine.conversion.viewToModel}, {@link engine.conversion.ViewConsumable}.
*
- * This is an abstract base class for all commands.
+ * Using this API it is possible to create various kind of converters:
*
- * @memberOf core.command
- * @mixes utils.ObservableMixin
+ * 1. View element to model element:
+ *
+ * buildViewConverter().for( dispatcher ).fromElement( 'p' ).toElement( 'paragraph' );
+ *
+ * 2. View element to model attribute:
+ *
+ * buildViewConverter().for( dispatcher ).fromElement( 'b' ).fromElement( 'strong' ).toAttribute( 'bold', 'true' );
+ *
+ * 3. View attribute to model attribute:
+ *
+ * buildViewConverter().for( dispatcher ).fromAttribute( 'style', { 'font-weight': 'bold' } ).toAttribute( 'bold', 'true' );
+ * buildViewConverter().for( dispatcher )
+ * .fromAttribute( 'class' )
+ * .toAttribute( ( viewElement ) => ( { class: viewElement.getAttribute( 'class' ) } ) );
+ *
+ * 4. View elements and attributes to model attribute:
+ *
+ * buildViewConverter().for( dispatcher )
+ * .fromElement( 'b' ).fromElement( 'strong' ).fromAttribute( 'style', { 'font-weight': 'bold' } )
+ * .toAttribute( 'bold', 'true' );
+ *
+ * 5. View {@link engine.view.Matcher view element matcher instance} or {@link engine.view.Matcher#add matcher pattern}
+ * to model element or attribute:
+ *
+ * const matcher = new ViewMatcher();
+ * matcher.add( 'div', { class: 'quote' } );
+ * buildViewConverter().for( dispatcher ).from( matcher ).toElement( 'quote' );
+ *
+ * buildViewConverter().for( dispatcher ).from( { name: 'span', class: 'bold' } ).toAttribute( 'bold', 'true' );
+ *
+ * Note, that converters built using `ViewConverterBuilder` automatically check {@link engine.model.Schema schema}
+ * if created model structure is valid. If given conversion would be invalid according to schema, it is ignored.
+ *
+ * It is possible to provide creator functions as parameters for {@link engine.conversion.ViewConverterBuilder#toElement}
+ * and {@link engine.conversion.ViewConverterBuilder#toAttribute} methods. See their descriptions to learn more.
+ *
+ * By default, converter will {@link engine.conversion.ViewConsumable#consume consume} every value specified in
+ * given `from...` query, i.e. `.from( { name: 'span', class: 'bold' } )` will make converter consume both `span` name
+ * and `bold` class. It is possible to change this behavior using {@link engine.conversion.ViewConverterBuilder#consuming consuming}
+ * modifier. The modifier alters the last `fromXXX` query used before it. To learn more about consuming values,
+ * see {@link engine.conversion.ViewConsumable}.
+ *
+ * It is also possible to {@link engine.conversion.ViewConverterBuilder#withPriority change default priority}
+ * of created converters to decide which converter should be fired earlier and which later. This is useful if you provide
+ * a general converter but want to provide different converter for a specific-case (i.e. given view element is converted
+ * always to given model element, but if it has given class it is converter to other model element). For this,
+ * use {@link engine.conversion.ViewConverterBuilder#withPriority withPriority} modifier. The modifier alters
+ * the last `from...` query used before it.
+ *
+ * Note that `to...` methods are "terminators", which means that should be the last one used in building converter.
+ *
+ * You can use {@link engine.conversion.ModelConverterBuilder} to create "opposite" converters - from model to view.
+ *
+ * @memberOf engine.conversion
*/
-var Command = function () {
+var ViewConverterBuilder = function () {
/**
- * Creates a new Command instance.
- *
- * @param {core.editor.Editor} editor Editor on which this command will be used.
+ * Creates `ViewConverterBuilder` with given `dispatchers` registered to it.
*/
- function Command(editor) {
- var _this = this;
-
- classCallCheck(this, Command);
+ function ViewConverterBuilder() {
+ classCallCheck(this, ViewConverterBuilder);
/**
- * Editor on which this command will be used.
+ * Dispatchers to which converters will be attached.
*
- * @readonly
- * @member {core.editor.Editor} core.command.Command#editor
+ * @type {Array.}
+ * @private
*/
- this.editor = editor;
+ this._dispatchers = [];
/**
- * Flag indicating whether a command is enabled or disabled.
- * A disabled command should do nothing upon it's execution.
+ * Stores "from" queries.
*
- * @observable
- * @member {Boolean} core.command.Command#isEnabled
+ * @type {Array}
+ * @private
*/
- this.set('isEnabled', true);
-
- // If schema checking function is specified, add it to the `refreshState` listeners.
- // Feature will be disabled if it does not apply to schema requirements.
- if (this._checkEnabled) {
- this.on('refreshState', function (evt, data) {
- data.isEnabled = _this._checkEnabled();
- });
- }
+ this._from = [];
}
- createClass(Command, [{
- key: 'destroy',
- value: function destroy() {
- this.stopListening();
- }
-
- /**
- * Fires `refreshState` event and checks it's resolve value to decide whether command should be enabled or not.
- * Other parts of code might listen to `refreshState` event on this command and add their callbacks. This
- * way the responsibility of deciding whether a command should be enabled is shared.
- *
- * @fires {@link core.command.Command#refreshState refreshState}
- */
-
- }, {
- key: 'refreshState',
- value: function refreshState() {
- var data = { isEnabled: true };
- this.fire('refreshState', data);
-
- this.isEnabled = data.isEnabled;
- }
+ /**
+ * Set one or more dispatchers which the built converter will be attached to.
+ *
+ * @chainable
+ * @param {...engine.conversion.ViewConversionDispatcher} dispatchers One or more dispatchers.
+ * @returns {engine.conversion.ViewConverterBuilder}
+ */
- /**
- * Executes the command if it is enabled.
- *
- * @protected
- * @param {*} param Parameter passed to {@link core.command.Command#execute execute} method of this command.
- */
- }, {
- key: '_execute',
- value: function _execute(param) {
- if (this.isEnabled) {
- this._doExecute(param);
+ createClass(ViewConverterBuilder, [{
+ key: 'for',
+ value: function _for() {
+ for (var _len = arguments.length, dispatchers = Array(_len), _key = 0; _key < _len; _key++) {
+ dispatchers[_key] = arguments[_key];
}
- }
-
- /**
- * Disables the command. This should be used only by the command itself. Other parts of code should add
- * listeners to `refreshState` event.
- *
- * @protected
- */
-
- }, {
- key: '_disable',
- value: function _disable() {
- this.on('refreshState', disableCallback);
- this.refreshState();
- }
-
- /**
- * Enables the command (internally). This should be used only by the command itself. Command will be enabled if
- * other listeners does not return false on `refreshState` event callbacks. Firing {@link core.command.Command#_enable}
- * does not guarantee that {@link core.command.Command#isEnabled} will be set to true, as it depends on other listeners.
- *
- * @protected
- */
-
- }, {
- key: '_enable',
- value: function _enable() {
- this.off('refreshState', disableCallback);
- this.refreshState();
- }
-
- /**
- * Executes command.
- * This is an abstract method that should be overwritten in child classes.
- *
- * @protected
- */
-
- }, {
- key: '_doExecute',
- value: function _doExecute() {}
-
- /**
- * Checks if a command should be enabled according to its own rules. Mostly it will check schema to see if the command
- * is allowed to be executed in given position. This method can be defined in child class (but is not obligatory).
- * If it is defined, it will be added as a callback to `refreshState` event.
- *
- * @protected
- * @method core.command.Command#_checkEnabled
- * @returns {Boolean} `true` if command should be enabled according to {@link engine.model.Document#schema}. `false` otherwise.
- */
-
- }]);
- return Command;
-}();
-
-function disableCallback(evt, data) {
- data.isEnabled = false;
-}
-
-mix(Command, ObservableMixin);
-
-/**
- * Fired whenever command has to have its {@link core.command.Command#isEnabled} property refreshed. Every feature,
- * command or other class which needs to disable command (set `isEnabled` to `false`) should listen to this
- * event.
- *
- * @event core.command.Command#refreshState
- * @param {Object} data
- * @param {Boolean} [data.isEnabled=true]
- */
-
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * The heading command. It is used by the {@link heading.Heading heading feature} to apply headings.
- *
- * @memberOf heading
- * @extends core.command.Command
- */
-var HeadingCommand = function (_Command) {
- inherits(HeadingCommand, _Command);
+ this._dispatchers = dispatchers;
- /**
- * Creates an instance of the command.
- *
- * @param {core.editor.Editor} editor Editor instance.
- * @param {Array.} formats Heading formats to be used by the command instance.
- */
- function HeadingCommand(editor, formats) {
- classCallCheck(this, HeadingCommand);
+ return this;
+ }
/**
- * Heading formats used by this command.
+ * Registers what view element should be converted.
*
- * @readonly
- * @member {heading.HeadingFormat} heading.HeadingCommand#formats
+ * buildViewConverter().for( dispatcher ).fromElement( 'p' ).toElement( 'paragraph' );
+ *
+ * @chainable
+ * @param {String} elementName View element name.
+ * @returns {engine.conversion.ViewConverterBuilder}
*/
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(HeadingCommand).call(this, editor));
- _this.formats = formats;
+ }, {
+ key: 'fromElement',
+ value: function fromElement(elementName) {
+ return this.from({ name: elementName });
+ }
/**
- * The currently selected heading format.
+ * Registers what view attribute should be converted.
*
- * @readonly
- * @observable
- * @member {heading.HeadingFormat} heading.HeadingCommand#value
+ * buildViewConverter().for( dispatcher ).fromAttribute( 'style', { 'font-weight': 'bold' } ).toAttribute( 'bold', 'true' );
+ *
+ * @chainable
+ * @param {String|RegExp} key View attribute key.
+ * @param {String|RegExp} [value] View attribute value.
+ * @returns {engine.conversion.ViewConverterBuilder}
*/
- _this.set('value', _this.defaultFormat);
- // Listen on selection change and set current command's format to format in the current selection.
- _this.listenTo(editor.document.selection, 'change', function () {
- var position = editor.document.selection.getFirstPosition();
- var block = findTopmostBlock(position);
+ }, {
+ key: 'fromAttribute',
+ value: function fromAttribute(key) {
+ var value = arguments.length <= 1 || arguments[1] === undefined ? /.*/ : arguments[1];
- if (block) {
- var format = _this._getFormatById(block.name);
+ var pattern = {};
+ pattern[key] = value;
- // TODO: What should happen if format is not found?
- _this.value = format;
- }
- });
- return _this;
- }
+ return this.from(pattern);
+ }
- /**
- * The default format.
- *
- * @type {heading.HeadingFormat}
- */
+ /**
+ * Registers what view pattern should be converted. The method accepts either {@link engine.view.Matcher view matcher}
+ * or view matcher pattern.
+ *
+ * const matcher = new ViewMatcher();
+ * matcher.add( 'div', { class: 'quote' } );
+ * buildViewConverter().for( dispatcher ).from( matcher ).toElement( 'quote' );
+ *
+ * buildViewConverter().for( dispatcher ).from( { name: 'span', class: 'bold' } ).toAttribute( 'bold', 'true' );
+ *
+ * @chainable
+ * @param {Object|engine.view.Matcher} matcher View matcher or view matcher pattern.
+ * @returns {engine.conversion.ViewConverterBuilder}
+ */
+ }, {
+ key: 'from',
+ value: function from(matcher) {
+ if (!(matcher instanceof Matcher)) {
+ matcher = new Matcher(matcher);
+ }
- createClass(HeadingCommand, [{
- key: '_doExecute',
+ this._from.push({
+ matcher: matcher,
+ consume: false,
+ priority: null
+ });
+ return this;
+ }
/**
- * Executes command.
+ * Modifies which consumable values will be {@link engine.conversion.ViewConsumable#consume consumed} by built converter.
+ * It modifies the last `from...` query. Can be used after each `from...` query in given chain. Useful for providing
+ * more specific matches.
*
- * @protected
- * @param {Object} [options] Options for executed command.
- * @param {String} [options.formatId] The identifier of the heading format that should be applied. It should be one of the
- * {@link heading.HeadingFormat heading formats} provided to the command constructor. If this parameter is not provided,
- * the value from {@link heading.HeadingCommand#defaultFormat defaultFormat} will be used.
- * @param {engine.model.Batch} [options.batch] Batch to collect all the change steps.
- * New batch will be created if this option is not set.
+ * // This converter will only handle class bold conversion (to proper attribute) but span element
+ * // conversion will have to be done in separate converter.
+ * // Without consuming modifier, the converter would consume both class and name, so a converter for
+ * // span element would not be fired.
+ * buildViewConverter().for( dispatcher )
+ * .from( { name: 'span', class: 'bold' } ).consuming( { class: 'bold' } )
+ * .toAttribute( 'bold', 'true' } );
+ *
+ * buildViewConverter().for( dispatcher )
+ * .fromElement( 'img' ).consuming( { name: true, attributes: [ 'src', 'title' ] } )
+ * .toElement( ( viewElement ) => new ModelElement( 'image', { src: viewElement.getAttribute( 'src' ),
+ * title: viewElement.getAttribute( 'title' ) } );
+ *
+ * **Note:** All and only values from passed object has to be consumable on converted view element. This means that
+ * using `consuming` method, you can either make looser conversion conditions (like in first example) or tighter
+ * conversion conditions (like in second example). So, the view element, to be converter, has to match query of
+ * `from...` method and then have to have enough consumable values to consume.
+ *
+ * @see engine.conversion.ViewConsumable
+ * @chainable
+ * @param {Object} consume Values to consume.
+ * @returns {engine.conversion.ViewConverterBuilder}
*/
- value: function _doExecute() {
- var _this2 = this;
- var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
+ }, {
+ key: 'consuming',
+ value: function consuming(consume) {
+ var lastFrom = this._from[this._from.length - 1];
+ lastFrom.consume = consume;
- // TODO: What should happen if format is not found?
- var formatId = options.formatId || this.defaultFormat.id;
- var doc = this.editor.document;
- var selection = doc.selection;
- var startPosition = selection.getFirstPosition();
- var elements = [];
- // Storing selection ranges and direction to fix selection after renaming. See ckeditor5-engine#367.
- var ranges = [].concat(toConsumableArray(selection.getRanges()));
- var isSelectionBackward = selection.isBackward;
- // If current format is same as new format - toggle already applied format back to default one.
- var shouldRemove = formatId === this.value.id;
+ return this;
+ }
- // Collect elements to change format.
- // This implementation may not be future proof but it's satisfactory at this stage.
- if (selection.isCollapsed) {
- var block = findTopmostBlock(startPosition);
+ /**
+ * Changes default priority for built converter. It modifies the last `from...` query. Can be used after each
+ * `from...` query in given chain. Useful for overwriting converters. The lower the number, the earlier converter will be fired.
+ *
+ * buildViewConverter().for( dispatcher ).fromElement( 'p' ).toElement( 'paragraph' );
+ * // Register converter with proper priority, otherwise "p" element would get consumed by first
+ * // converter and the second converter would not be fired.
+ * buildViewConverter().for( dispatcher )
+ * .from( { name: 'p', class: 'custom' } ).withPriority( 9 )
+ * .toElement( 'customParagraph' );
+ *
+ * **Note:** `ViewConverterBuilder` takes care so all `toElement` conversions takes place before all `toAttribute`
+ * conversions. This is done by setting default `toElement` priority to `10` and `toAttribute` priority to `1000`.
+ * It is recommended to set converter priority for `toElement` conversions below `500` and `toAttribute` priority
+ * above `500`. It is important that model elements are created before attributes, otherwise attributes would
+ * not be applied or other errors may occur.
+ *
+ * @chainable
+ * @param {Number} priority Converter priority.
+ * @returns {engine.conversion.ViewConverterBuilder}
+ */
- if (block) {
- elements.push(block);
- }
- } else {
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+ }, {
+ key: 'withPriority',
+ value: function withPriority(priority) {
+ var lastFrom = this._from[this._from.length - 1];
+ lastFrom.priority = priority;
- try {
- for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var range = _step.value;
+ return this;
+ }
- var startBlock = findTopmostBlock(range.start);
- var endBlock = findTopmostBlock(range.end, false);
+ /**
+ * Registers what model element will be created by converter.
+ *
+ * Method accepts two ways of providing what kind of model element will be created. You can pass model element
+ * name as a `string` or a function that will return model element instance. If you provide creator function,
+ * it will be passed converted view element as first and only parameter.
+ *
+ * buildViewConverter().for( dispatcher ).fromElement( 'p' ).toElement( 'paragraph' );
+ * buildViewConverter().for( dispatcher )
+ * .fromElement( 'img' )
+ * .toElement( ( viewElement ) => new ModelElement( 'image', { src: viewElement.getAttribute( 'src' ) } );
+ *
+ * @param {String|Function} element Model element name or model element creator function.
+ */
- elements.push(startBlock);
+ }, {
+ key: 'toElement',
+ value: function toElement(element) {
+ var eventCallbackGen = function eventCallbackGen(from) {
+ return function (evt, data, consumable, conversionApi) {
+ // There is one callback for all patterns in the matcher.
+ // This will be usually just one pattern but we support matchers with many patterns too.
+ var matchAll = from.matcher.matchAll(data.input);
- while (startBlock !== endBlock) {
- startBlock = startBlock.nextSibling;
- elements.push(startBlock);
- }
+ // If there is no match, this callback should not do anything.
+ if (!matchAll) {
+ return;
}
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
+
+ // Now, for every match between matcher and actual element, we will try to consume the match.
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
- }
+ for (var _iterator = matchAll[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var match = _step.value;
+
+ // Create model element basing on creator function or element name.
+ var modelElement = element instanceof Function ? element(data.input) : new Element(element);
+
+ // Check whether generated structure is okay with `Schema`.
+ var keys = Array.from(modelElement.getAttributeKeys());
+
+ if (!conversionApi.schema.check({ name: modelElement.name, attributes: keys, inside: data.context })) {
+ continue;
+ }
+
+ // Try to consume appropriate values from consumable values list.
+ if (!consumable.consume(data.input, from.consume || match.match)) {
+ continue;
+ }
- doc.enqueueChanges(function () {
- var batch = options.batch || doc.batch();
+ // If everything is fine, we are ready to start the conversion.
+ // Add newly created `modelElement` to the parents stack.
+ data.context.push(modelElement);
- var _iteratorNormalCompletion2 = true;
- var _didIteratorError2 = false;
- var _iteratorError2 = undefined;
+ // Convert children of converted view element and append them to `modelElement`.
+ var modelChildren = conversionApi.convertChildren(data.input, consumable, data);
+ var insertPosition = Position.createAt(modelElement, 'end');
+ modelWriter.insert(insertPosition, modelChildren);
- try {
- for (var _iterator2 = elements[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- var element = _step2.value;
+ // Remove created `modelElement` from the parents stack.
+ data.context.pop();
- // When removing applied format.
- if (shouldRemove) {
- if (element.name === formatId) {
- batch.rename(element, _this2.defaultFormat.id);
- }
- }
- // When applying new format.
- else {
- batch.rename(element, formatId);
- }
- }
+ // Add `modelElement` as a result.
+ data.output = modelElement;
- // If range's selection start/end is placed directly in renamed block - we need to restore it's position
- // after renaming, because renaming puts new element there.
- } catch (err) {
- _didIteratorError2 = true;
- _iteratorError2 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion2 && _iterator2.return) {
- _iterator2.return();
+ // Prevent multiple conversion if there are other correct matches.
+ break;
}
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
} finally {
- if (_didIteratorError2) {
- throw _iteratorError2;
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
}
}
- }
+ };
+ };
- doc.selection.setRanges(ranges, isSelectionBackward);
- });
+ this._setCallback(eventCallbackGen, 'normal');
}
/**
- * Returns the format by a given ID.
+ * Registers what model attribute will be created by converter.
*
- * @private
- * @param {String} id
- * @returns {heading.HeadingFormat}
+ * Method accepts two ways of providing what kind of model attribute will be created. You can either pass two strings
+ * representing attribute key and attribute value or a function that returns an object with `key` and `value` properties.
+ * If you provide creator function, it will be passed converted view element as first and only parameter.
+ *
+ * buildViewConverter().for( dispatcher ).fromAttribute( 'style', { 'font-weight': 'bold' } ).toAttribute( 'bold', 'true' );
+ * buildViewConverter().for( dispatcher )
+ * .fromAttribute( 'class' )
+ * .toAttribute( ( viewElement ) => ( { key: 'class', value: viewElement.getAttribute( 'class' ) } ) );
+ *
+ * @param {String|Function} keyOrCreator Attribute key or a creator function.
+ * @param {String} [value] Attribute value. Required if `keyOrCreator` is a `string`. Ignored otherwise.
*/
}, {
- key: '_getFormatById',
- value: function _getFormatById(id) {
- return this.formats.find(function (item) {
- return item.id === id;
- }) || this.defaultFormat;
- }
- }, {
- key: 'defaultFormat',
- get: function get() {
- // See https://github.com/ckeditor/ckeditor5/issues/98.
- return this._getFormatById('paragraph');
- }
- }]);
- return HeadingCommand;
-}(Command);
-
-function findTopmostBlock(position) {
- var nodeAfter = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
-
- var parent = position.parent;
-
- // If position is placed inside root - get element after/before it.
- if (parent instanceof RootElement) {
- return nodeAfter ? position.nodeAfter : position.nodeBefore;
- }
-
- while (!(parent.parent instanceof RootElement)) {
- parent = parent.parent;
- }
+ key: 'toAttribute',
+ value: function toAttribute(keyOrCreator, value) {
+ var eventCallbackGen = function eventCallbackGen(from) {
+ return function (evt, data, consumable, conversionApi) {
+ // There is one callback for all patterns in the matcher.
+ // This will be usually just one pattern but we support matchers with many patterns too.
+ var matchAll = from.matcher.matchAll(data.input);
- return parent;
-}
+ // If there is no match, this callback should not do anything.
+ if (!matchAll) {
+ return;
+ }
-/**
- * Heading format descriptor.
- *
- * @typedef {Object} heading.HeadingFormat
- * @property {String} id Format identifier. It will be used as the element's name in the model.
- * @property {String} viewElement The name of the view element that will be used to represent the model element in the view.
- * @property {String} label The display name of the format.
- */
+ // Now, for every match between matcher and actual element, we will try to consume the match.
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ try {
+ for (var _iterator2 = matchAll[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var match = _step2.value;
-var formats = [{ id: 'paragraph', viewElement: 'p', label: 'Paragraph' }, { id: 'heading1', viewElement: 'h2', label: 'Heading 1' }, { id: 'heading2', viewElement: 'h3', label: 'Heading 2' }, { id: 'heading3', viewElement: 'h4', label: 'Heading 3' }];
+ // Try to consume appropriate values from consumable values list.
+ if (!consumable.consume(data.input, from.consume || match.match)) {
+ continue;
+ }
-/**
- * The headings engine feature. It handles switching between block formats – headings and paragraph.
- * This class represents the engine part of the heading feature. See also {@link heading.Heading}.
- *
- * @memberOf heading
- * @extends core.Feature
- */
+ // Since we are converting to attribute we need an output on which we will set the attribute.
+ // If the output is not created yet, we will create it.
+ if (!data.output) {
+ data.output = conversionApi.convertChildren(data.input, consumable, data);
+ }
-var HeadingEngine = function (_Feature) {
- inherits(HeadingEngine, _Feature);
+ // Use attribute creator function, if provided.
+ var attribute = keyOrCreator instanceof Function ? keyOrCreator(data.input) : { key: keyOrCreator, value: value };
- function HeadingEngine() {
- classCallCheck(this, HeadingEngine);
- return possibleConstructorReturn(this, Object.getPrototypeOf(HeadingEngine).apply(this, arguments));
- }
+ // Set attribute on current `output`. `Schema` is checked inside this helper function.
+ setAttributeOn(data.output, attribute, data, conversionApi);
- createClass(HeadingEngine, [{
- key: 'init',
+ // Prevent multiple conversion if there are other correct matches.
+ break;
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+ };
+ };
+ this._setCallback(eventCallbackGen, 'low');
+ }
/**
- * @inheritDoc
+ * Helper function that uses given callback generator to created callback function and sets it on registered dispatchers.
+ *
+ * @param eventCallbackGen
+ * @param defaultPriority
+ * @private
*/
- value: function init() {
- var editor = this.editor;
- var data = editor.data;
- var editing = editor.editing;
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+ }, {
+ key: '_setCallback',
+ value: function _setCallback(eventCallbackGen, defaultPriority) {
+ // We will add separate event callback for each registered `from` entry.
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
try {
- for (var _iterator = formats[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var format = _step.value;
+ for (var _iterator3 = this._from[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var from = _step3.value;
- // Skip paragraph - it is defined in required Paragraph feature.
- if (format.id !== 'paragraph') {
- // Schema.
- editor.document.schema.registerItem(format.id, '$block');
+ // We have to figure out event name basing on matcher's patterns.
+ // If there is exactly one pattern and it has `name` property we will used that name.
+ var matcherElementName = from.matcher.getElementName();
+ var eventName = matcherElementName ? 'element:' + matcherElementName : 'element';
+ var eventCallback = eventCallbackGen(from);
- // Build converter from model to view for data and editing pipelines.
- buildModelConverter().for(data.modelToView, editing.modelToView).fromElement(format.id).toElement(format.viewElement);
+ var priority = from.priority === null ? defaultPriority : from.priority;
- // Build converter from view to model for data pipeline.
- buildViewConverter().for(data.viewToModel).fromElement(format.viewElement).toElement(format.id);
- }
- }
+ // Add event to each registered dispatcher.
+ var _iteratorNormalCompletion4 = true;
+ var _didIteratorError4 = false;
+ var _iteratorError4 = undefined;
- // Register the heading command.
+ try {
+ for (var _iterator4 = this._dispatchers[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
+ var dispatcher = _step4.value;
+
+ dispatcher.on(eventName, eventCallback, { priority: priority });
+ }
+ } catch (err) {
+ _didIteratorError4 = true;
+ _iteratorError4 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion4 && _iterator4.return) {
+ _iterator4.return();
+ }
+ } finally {
+ if (_didIteratorError4) {
+ throw _iteratorError4;
+ }
+ }
+ }
+ }
} catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
} finally {
try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
+ if (!_iteratorNormalCompletion3 && _iterator3.return) {
+ _iterator3.return();
}
} finally {
- if (_didIteratorError) {
- throw _iteratorError;
+ if (_didIteratorError3) {
+ throw _iteratorError3;
}
}
}
-
- var command = new HeadingCommand(editor, formats);
- editor.commands.set('heading', command);
-
- // If the enter command is added to the editor, alter its behavior.
- // Enter at the end of a heading element should create a paragraph.
- var enterCommand = editor.commands.get('enter');
-
- if (enterCommand) {
- this.listenTo(enterCommand, 'afterExecute', function (evt, data) {
- var positionParent = editor.document.selection.getFirstPosition().parent;
- var batch = data.batch;
- var isHeading = formats.some(function (format) {
- return format.id == positionParent.name;
- });
-
- if (isHeading && positionParent.name != command.defaultFormat.id && positionParent.childCount === 0) {
- batch.rename(positionParent, command.defaultFormat.id);
- }
- });
- }
- }
- }], [{
- key: 'requires',
-
- /**
- * @inheritDoc
- */
- get: function get() {
- return [Paragraph];
}
}]);
- return HeadingEngine;
-}(Feature);
+ return ViewConverterBuilder;
+}();
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+// Helper function that sets given attributes on given `engine.model.Node` or `engine.model.DocumentFragment`.
-/**
- * Utilities used in modules from {@link list list} package.
- *
- * @memberOf list
- * @namespace list.utils
- */
-/**
- * For given {@link engine.model.Position position}, returns the closest ancestor of that position which is a
- * `listItem` element.
- *
- * @function list.utils.getClosestListItem
- * @param {engine.model.Position} position Position which ancestor should be check looking for `listItem` element.
- * @returns {engine.model.Element|null} Element with `listItem` name that is a closest ancestor of given `position`, or
- * `null` if neither of `position` ancestors is a `listItem`.
- */
-function getClosestListItem(position) {
- return Array.from(position.getAncestors()).find(function (parent) {
- return parent.name == 'listItem';
- }) || null;
-}
+function setAttributeOn(toChange, attribute, data, conversionApi) {
+ if (isIterable(toChange)) {
+ var _iteratorNormalCompletion5 = true;
+ var _didIteratorError5 = false;
+ var _iteratorError5 = undefined;
-/**
- * For given {@link engine.model.Selection selection} and {@link engine.model.Schema schema}, returns an array with
- * all elements that are in the selection and are extending `$block` schema item.
- *
- * @function list.utils.getSelectedBlocks
- * @param {engine.model.Selection} selection Selection from which blocks will be taken.
- * @param {engine.model.Schema} schema Schema which will be used to check if a model element extends `$block`.
- * @returns {Array.} All blocks from the selection.
- */
-function getSelectedBlocks(selection, schema) {
- var position = getPositionBeforeBlock(selection.getFirstPosition(), schema);
+ try {
+ for (var _iterator5 = toChange[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
+ var node = _step5.value;
- var endPosition = selection.getLastPosition();
- var blocks = [];
+ setAttributeOn(node, attribute, data, conversionApi);
+ }
+ } catch (err) {
+ _didIteratorError5 = true;
+ _iteratorError5 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion5 && _iterator5.return) {
+ _iterator5.return();
+ }
+ } finally {
+ if (_didIteratorError5) {
+ throw _iteratorError5;
+ }
+ }
+ }
- // Traverse model from the first position before a block to the end position of selection.
- // Store all elements that were after the correct positions.
- while (position !== null && position.isBefore(endPosition)) {
- blocks.push(position.nodeAfter);
+ return;
+ }
- position.offset++;
- position = getPositionBeforeBlock(position, schema);
- }
+ var keys = Array.from(toChange.getAttributeKeys());
+ keys.push(attribute.key);
- return blocks;
+ var schemaQuery = {
+ name: toChange.name || '$text',
+ attributes: keys,
+ inside: data.context
+ };
+
+ if (conversionApi.schema.check(schemaQuery)) {
+ toChange.setAttribute(attribute.key, attribute.value);
+ }
}
/**
- * For given {@link engine.model.Position position}, finds a model element extending `$block` schema item which is
- * closest element to that position. First node after the position is checked and then the position's ancestors. `null`
- * is returned if such element has not been found or found element is a root element.
+ * Entry point for view-to-model converters builder. This chainable API makes it easy to create basic, most common
+ * view-to-model converters and attach them to provided dispatchers. The method returns an instance of
+ * {@link engine.conversion.ViewConverterBuilder}.
*
- * @param position
- * @param schema
- * @returns {*}
+ * @external engine.conversion.buildViewConverter
+ * @memberOf engine.conversion
*/
-function getPositionBeforeBlock(position, schema) {
- // Start from the element right after the position. Maybe it is already a `$block` element.
- var element = position.nodeAfter;
-
- // If the position is not before an element, check the parent.
- if (!element) {
- element = position.parent;
- }
-
- // If proper element is still not found, check the ancestors.
- while (element !== null && !schema.itemExtends(element.name || '$text', '$block')) {
- element = element.parent;
- }
-
- // If proper element has been found, return position before it, otherwise return null;
- return element !== null && element.parent !== null ? Position.createBefore(element) : null;
+function buildViewConverter() {
+ return new ViewConverterBuilder();
}
/**
@@ -44749,265 +43698,301 @@ function getPositionBeforeBlock(position, schema) {
*/
/**
- * The list command. It is used by the {@link list.List list feature}.
+ * The base class for CKEditor commands.
*
- * @memberOf list
- * @extends core.command.Command
+ * Commands are main way to manipulate editor contents and state. They are mostly used by UI elements (or by other
+ * commands) to make changes in Tree Model. Commands are available in every part of code that has access to
+ * {@link core.editor.Editor} instance, since they are registered in it and executed through {@link core.editor.Editor#execute}.
+ * Commands instances are available through {@link core.editor.Editor#commands}.
+ *
+ * This is an abstract base class for all commands.
+ *
+ * @memberOf core.command
+ * @mixes utils.ObservableMixin
*/
-var ListCommand = function (_Command) {
- inherits(ListCommand, _Command);
-
+var Command = function () {
/**
- * Creates an instance of the command.
+ * Creates a new Command instance.
*
- * @param {core.editor.Editor} editor Editor instance.
- * @param {'numbered'|'bulleted'} type List type that will be handled by this command.
+ * @param {core.editor.Editor} editor Editor on which this command will be used.
*/
- function ListCommand(editor, type) {
- classCallCheck(this, ListCommand);
+ function Command(editor) {
+ var _this = this;
+
+ classCallCheck(this, Command);
/**
- * The type of list created by the command.
+ * Editor on which this command will be used.
*
* @readonly
- * @member {'numbered'|'bulleted'} list.ListCommand#type
+ * @member {core.editor.Editor} core.command.Command#editor
*/
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListCommand).call(this, editor));
-
- _this.type = type == 'bulleted' ? 'bulleted' : 'numbered';
+ this.editor = editor;
/**
- * Flag indicating whether the command is active, which means that selection starts in a list of the same type.
+ * Flag indicating whether a command is enabled or disabled.
+ * A disabled command should do nothing upon it's execution.
*
* @observable
- * @member {Boolean} list.ListCommand#value
+ * @member {Boolean} core.command.Command#isEnabled
*/
- _this.set('value', false);
-
- var changeCallback = function changeCallback() {
- _this.refreshValue();
- _this.refreshState();
- };
+ this.set('isEnabled', true);
- // Listen on selection and document changes and set the current command's value.
- _this.listenTo(editor.document.selection, 'change:range', changeCallback);
- _this.listenTo(editor.document, 'changesDone', changeCallback);
- return _this;
+ // If schema checking function is specified, add it to the `refreshState` listeners.
+ // Feature will be disabled if it does not apply to schema requirements.
+ if (this._checkEnabled) {
+ this.on('refreshState', function (evt, data) {
+ data.isEnabled = _this._checkEnabled();
+ });
+ }
}
- /**
- * Sets command's value based on the document selection.
- */
+ createClass(Command, [{
+ key: 'destroy',
+ value: function destroy() {
+ this.stopListening();
+ }
+
+ /**
+ * Fires `refreshState` event and checks it's resolve value to decide whether command should be enabled or not.
+ * Other parts of code might listen to `refreshState` event on this command and add their callbacks. This
+ * way the responsibility of deciding whether a command should be enabled is shared.
+ *
+ * @fires {@link core.command.Command#refreshState refreshState}
+ */
+
+ }, {
+ key: 'refreshState',
+ value: function refreshState() {
+ var data = { isEnabled: true };
+ this.fire('refreshState', data);
+
+ this.isEnabled = data.isEnabled;
+ }
+
+ /**
+ * Executes the command if it is enabled.
+ *
+ * @protected
+ * @param {*} param Parameter passed to {@link core.command.Command#execute execute} method of this command.
+ */
+
+ }, {
+ key: '_execute',
+ value: function _execute(param) {
+ if (this.isEnabled) {
+ this._doExecute(param);
+ }
+ }
+
+ /**
+ * Disables the command. This should be used only by the command itself. Other parts of code should add
+ * listeners to `refreshState` event.
+ *
+ * @protected
+ */
+ }, {
+ key: '_disable',
+ value: function _disable() {
+ this.on('refreshState', disableCallback);
+ this.refreshState();
+ }
- createClass(ListCommand, [{
- key: 'refreshValue',
- value: function refreshValue() {
- var position = this.editor.document.selection.getFirstPosition();
+ /**
+ * Enables the command (internally). This should be used only by the command itself. Command will be enabled if
+ * other listeners does not return false on `refreshState` event callbacks. Firing {@link core.command.Command#_enable}
+ * does not guarantee that {@link core.command.Command#isEnabled} will be set to true, as it depends on other listeners.
+ *
+ * @protected
+ */
- // Check whether closest `listItem` ancestor of the position has a correct type.
- var listItem = getClosestListItem(position);
- this.value = listItem !== null && listItem.getAttribute('type') == this.type;
+ }, {
+ key: '_enable',
+ value: function _enable() {
+ this.off('refreshState', disableCallback);
+ this.refreshState();
}
/**
* Executes command.
+ * This is an abstract method that should be overwritten in child classes.
*
* @protected
- * @param {Object} [options] Options for executed command.
- * @param {engine.model.Batch} [options.batch] Batch to collect all the change steps.
- * New batch will be created if this option is not set.
*/
}, {
key: '_doExecute',
- value: function _doExecute() {
- var _this2 = this;
-
- var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
+ value: function _doExecute() {}
- var document = this.editor.document;
- var blocks = getSelectedBlocks(document.selection, document.schema);
+ /**
+ * Checks if a command should be enabled according to its own rules. Mostly it will check schema to see if the command
+ * is allowed to be executed in given position. This method can be defined in child class (but is not obligatory).
+ * If it is defined, it will be added as a callback to `refreshState` event.
+ *
+ * @protected
+ * @method core.command.Command#_checkEnabled
+ * @returns {Boolean} `true` if command should be enabled according to {@link engine.model.Document#schema}. `false` otherwise.
+ */
- // Whether we are turning off some items.
- var turnOff = this.value === true;
- // If we are turning off items, we are going to rename them to paragraphs.
+ }]);
+ return Command;
+}();
- document.enqueueChanges(function () {
- var batch = options.batch || document.batch();
+function disableCallback(evt, data) {
+ data.isEnabled = false;
+}
- // If part of a list got turned off, we need to handle (outdent) all of sub-items of the last turned-off item.
- // To be sure that model is all the time in a good state, we first fix items below turned-off item.
- if (turnOff) {
- // Start from the model item that is just after the last turned-off item.
- var next = blocks[blocks.length - 1].nextSibling;
- var currentIndent = Number.POSITIVE_INFINITY;
- var changes = [];
+mix(Command, ObservableMixin);
- // Correct indent of all items after the last turned off item.
- // Rules that should be followed:
- // 1. All direct sub-items of turned-off item should become indent 0, because the first item after it
- // will be the first item of a new list. Other items are at the same level, so should have same 0 index.
- // 2. All items with indent lower than indent of turned-off item should become indent 0, because they
- // should not end up as a child of any of list items that they were not children of before.
- // 3. All other items should have their indent changed relatively to it's parent.
- //
- // For example:
- // 1 * --------
- // 2 * --------
- // 3 * -------- <- this is turned off.
- // 4 * -------- <- this has to become indent = 0, because it will be first item on a new list.
- // 5 * -------- <- this should be still be a child of item above, so indent = 1.
- // 6 * -------- <- this also has to become indent = 0, because it shouldn't end up as a child of any of items above.
- // 7 * -------- <- this should be still be a child of item above, so indent = 1.
- // 8 * -------- <- this has to become indent = 0.
- // 9 * -------- <- this should still be a child of item above, so indent = 1.
- // 10 * -------- <- this should still be a child of item above, so indent = 2.
- // 11 * -------- <- this should still be at the same level as item above, so indent = 2.
- // 12 * -------- <- this and all below are left unchanged.
- // 13 * --------
- // 14 * --------
- //
- // After turning off 3 the list becomes:
- //
- // 1 * --------
- // 2 * --------
- //
- // 3 --------
- //
- // 4 * --------
- // 5 * --------
- // 6 * --------
- // 7 * --------
- // 8 * --------
- // 9 * --------
- // 10 * --------
- // 11 * --------
- // 12 * --------
- // 13 * --------
- // 14 * --------
- //
- // Thanks to this algorithm no lists are mismatched and no items get unexpected children/parent, while
- // those parent-child connection which are possible to maintain are still maintained. It's worth noting
- // that this is the same effect that we would be get by multiple use of outdent command. However doing
- // it like this is much more efficient because it's less operation (less memory usage, easier OT) and
- // less conversion (faster).
- while (next && next.name == 'listItem' && next.getAttribute('indent') !== 0) {
- // Check each next list item, as long as its indent is bigger than 0.
- // If the indent is 0 we are not going to change anything anyway.
- var indent = next.getAttribute('indent');
+/**
+ * Fired whenever command has to have its {@link core.command.Command#isEnabled} property refreshed. Every feature,
+ * command or other class which needs to disable command (set `isEnabled` to `false`) should listen to this
+ * event.
+ *
+ * @event core.command.Command#refreshState
+ * @param {Object} data
+ * @param {Boolean} [data.isEnabled=true]
+ */
- // We check if that's item indent is lower as current relative indent.
- if (indent < currentIndent) {
- // If it is, current relative indent becomes that indent.
- currentIndent = indent;
- }
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- // Fix indent relatively to current relative indent.
- // Note, that if we just changed the current relative indent, the newIndent will be equal to 0.
- var newIndent = indent - currentIndent;
+/**
+ * Walks through given array of ranges and removes parts of them that are not allowed by passed schema to have the
+ * attribute set. This is done by breaking a range in two and omitting the not allowed part.
+ *
+ * @param {String} attribute Attribute key.
+ * @param {Array.} ranges Ranges to be validated.
+ * @param {engine.model.Schema} schema Document schema.
+ * @returns {Array.} Ranges without invalid parts.
+ */
+function getSchemaValidRanges(attribute, ranges, schema) {
+ var validRanges = [];
- // Save the entry in changes array. We do not apply it at the moment, because we will need to
- // reverse the changes so the last item is changed first.
- // This is to keep model in correct state all the time.
- changes.push({ element: next, indent: newIndent });
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- // Find next item.
- next = next.nextSibling;
- }
+ try {
+ for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var range = _step.value;
- changes = changes.reverse();
+ var walker = new TreeWalker({ boundaries: range, mergeCharacters: true });
+ var step = walker.next();
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+ var last = range.start;
+ var from = range.start;
+ var to = range.end;
- try {
- for (var _iterator = changes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var item = _step.value;
+ while (!step.done) {
+ var name = step.value.item.name || '$text';
- batch.setAttribute(item.element, 'indent', item.indent);
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
+ if (!schema.check({ name: name, inside: last, attributes: attribute })) {
+ if (!from.isEqual(last)) {
+ validRanges.push(new Range$1(from, last));
}
+
+ from = walker.position;
}
- // Phew! Now it will be easier :).
- // For each block element that was in the selection, we will either: turn it to list item,
- // turn it to paragraph, or change it's type. Or leave it as it is.
- var _iteratorNormalCompletion2 = true;
- var _didIteratorError2 = false;
- var _iteratorError2 = undefined;
+ last = walker.position;
+ step = walker.next();
+ }
+
+ if (from && !from.isEqual(to)) {
+ validRanges.push(new Range$1(from, to));
+ }
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+
+ return validRanges;
+}
+
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * Checks {@link engine.model.Document#schema} if attribute is allowed in selection:
+ * * if selection is on range, the command is enabled if any of nodes in that range can have bold,
+ * * if selection is collapsed, the command is enabled if text with bold is allowed in that node.
+ *
+ * @param {String} attribute Attribute key.
+ * @param {engine.model.Selection} selection Selection which ranges will be validate.
+ * @param {engine.model.Schema} schema Document schema.
+ * @returns {Boolean}
+ */
+function isAttributeAllowedInSelection(attribute, selection, schema) {
+ if (selection.isCollapsed) {
+ // Check whether schema allows for a test with `attributeKey` in caret position.
+ return schema.check({ name: '$text', inside: selection.getFirstPosition(), attributes: attribute });
+ } else {
+ var ranges = selection.getRanges();
- try {
- for (var _iterator2 = blocks[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- var element = _step2.value;
+ // For all ranges, check nodes in them until you find a node that is allowed to have `attributeKey` attribute.
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- if (turnOff && element.name == 'listItem') {
- // We are turning off and the element is a `listItem` - it should be converted to `paragraph`.
- // The order is important to keep model in correct state.
- batch.rename(element, 'paragraph').removeAttribute(element, 'type').removeAttribute(element, 'indent');
- } else if (!turnOff && element.name != 'listItem') {
- // We are turning on and the element is not a `listItem` - it should be converted to `listItem`.
- // The order is important to keep model in correct state.
- batch.setAttribute(element, 'type', _this2.type).setAttribute(element, 'indent', 0).rename(element, 'listItem');
- } else if (!turnOff && element.name == 'listItem' && element.getAttribute('type') != _this2.type) {
- // We are turning on and the element is a `listItem` but has different type - change type.
- batch.setAttribute(element, 'type', _this2.type);
- }
- }
- } catch (err) {
- _didIteratorError2 = true;
- _iteratorError2 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion2 && _iterator2.return) {
- _iterator2.return();
- }
- } finally {
- if (_didIteratorError2) {
- throw _iteratorError2;
- }
- }
- }
- });
- }
+ try {
+ for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var range = _step.value;
- /**
- * @inheritDoc
- */
+ var walker = new TreeWalker({ boundaries: range, mergeCharacters: true });
+ var last = walker.position;
+ var step = walker.next();
- }, {
- key: '_checkEnabled',
- value: function _checkEnabled() {
- // If command is enabled it means that we are in list item, so the command should be enabled.
- if (this.value) {
- return true;
- }
+ // Walk the range.
+ while (!step.done) {
+ // If returned item does not have name property, it is a model.TextFragment.
+ var name = step.value.item.name || '$text';
- var selection = this.editor.document.selection;
- var schema = this.editor.document.schema;
- var position = getPositionBeforeBlock(selection.getFirstPosition(), schema);
+ if (schema.check({ name: name, inside: last, attributes: attribute })) {
+ // If we found a node that is allowed to have the attribute, return true.
+ return true;
+ }
- // Otherwise, check if list item can be inserted at the position start.
- return schema.check({ name: 'listItem', inside: position, attributes: ['type', 'indent'] });
+ last = walker.position;
+ step = walker.next();
+ }
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
}
- }]);
- return ListCommand;
-}(Command);
+ }
+
+ // If we haven't found such node, return false.
+ return false;
+}
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -45015,159 +44000,148 @@ var ListCommand = function (_Command) {
*/
/**
- * The list indent command. It is used by the {@link list.List list feature}.
+ * An extension of basic {@link core.command.Command} class, which provides utilities for a command that toggle a single
+ * attribute on a text or element with value `true`. ToggleAttributeCommand uses {@link engine.model.Document#selection}
+ * to decide which nodes (if any) should be changed, and applies or removes attributes from them.
+ * See {@link engine.view.Converter#execute} for more.
*
- * @memberOf list
- * @extends core.command.Command
+ * The command checks {@link engine.model.Document#schema} to decide if it should be enabled.
+ * See {@link engine.view.Converter#checkSchema} for more.
+ *
+ * @memberOf core.command
*/
-var IndentCommand = function (_Command) {
- inherits(IndentCommand, _Command);
+var ToggleAttributeCommand = function (_Command) {
+ inherits(ToggleAttributeCommand, _Command);
/**
- * Creates an instance of the command.
- *
- * @param {core.editor.Editor} editor Editor instance.
- * @param {'forward'|'backward'} indentDirection Direction of indent. If it is equal to `backward`, the command
- * will outdent a list item.
+ * @see core.command.Command
+ * @param {core.editor.Editor} editor
+ * @param {String} attributeKey Attribute that will be set by the command.
*/
- function IndentCommand(editor, indentDirection) {
- classCallCheck(this, IndentCommand);
+ function ToggleAttributeCommand(editor, attributeKey) {
+ classCallCheck(this, ToggleAttributeCommand);
/**
- * By how much the command will change list item's indent attribute.
+ * Attribute that will be set by the command.
*
- * @readonly
- * @private
- * @member {Number} list.IndentCommand#_indentBy
+ * @member {String} core.command.ToggleAttributeCommand#attributeKey
*/
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(IndentCommand).call(this, editor));
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ToggleAttributeCommand).call(this, editor));
- _this._indentBy = indentDirection == 'forward' ? 1 : -1;
+ _this.attributeKey = attributeKey;
- // Refresh command state after selection is changed or changes has been done to the document.
- _this.listenTo(editor.document.selection, 'change:range', function () {
- _this.refreshState();
- });
+ /**
+ * Flag indicating whether command is active. For collapsed selection it means that typed characters will have
+ * the command's attribute set. For range selection it means that all nodes inside have the attribute applied.
+ *
+ * @observable
+ * @member {Boolean} core.command.ToggleAttributeCommand#value
+ */
+ _this.set('value', false);
- _this.listenTo(editor.document, 'changesDone', function () {
- _this.refreshState();
+ _this.listenTo(_this.editor.document.selection, 'change:attribute', function () {
+ _this.value = _this.editor.document.selection.hasAttribute(_this.attributeKey);
});
return _this;
}
/**
- * @inheritDoc
+ * Checks if {@link engine.model.Document#schema} allows to create attribute in {@link engine.model.Document#selection}
+ *
+ * @private
+ * @returns {Boolean}
*/
- createClass(IndentCommand, [{
- key: '_doExecute',
- value: function _doExecute() {
- var _this2 = this;
-
- var doc = this.editor.document;
- var batch = doc.batch();
- var element = getClosestListItem(doc.selection.getFirstPosition());
-
- doc.enqueueChanges(function () {
- var oldIndent = element.getAttribute('indent');
+ createClass(ToggleAttributeCommand, [{
+ key: '_checkEnabled',
+ value: function _checkEnabled() {
+ var document = this.editor.document;
- var itemsToChange = [element];
+ return isAttributeAllowedInSelection(this.attributeKey, document.selection, document.schema);
+ }
- // Indenting a list item should also indent all the items that are already sub-items of indented item.
- var next = element.nextSibling;
+ /**
+ * Executes the command: adds or removes attributes to nodes or selection.
+ *
+ * If the command is active (`value == true`), it will remove attributes. Otherwise, it will set attributes.
+ *
+ * The execution result differs, depending on the {@link engine.model.Document#selection}:
+ * * if selection is on a range, the command applies the attribute on all nodes in that ranges
+ * (if they are allowed to have this attribute by the {@link engine.model.Schema schema}),
+ * * if selection is collapsed in non-empty node, the command applies attribute to the {@link engine.model.Document#selection}
+ * itself (note that typed characters copy attributes from selection),
+ * * if selection is collapsed in empty node, the command applies attribute to the parent node of selection (note
+ * that selection inherits all attributes from a node if it is in empty node).
+ *
+ * If the command is disabled (`isEnabled == false`) when it is executed, nothing will happen.
+ *
+ * @private
+ * @param {Object} [options] Options of command.
+ * @param {Boolean} [options.forceValue] If set it will force command behavior. If `true`, command will apply attribute,
+ * otherwise command will remove attribute. If not set, command will look for it's current value to decide what it should do.
+ * @param {engine.model.Batch} [options.batch] Batch to group undo steps.
+ */
- // Check all items as long as their indent is bigger than indent of changed list item.
- while (next && next.name == 'listItem' && next.getAttribute('indent') > oldIndent) {
- itemsToChange.push(next);
+ }, {
+ key: '_doExecute',
+ value: function _doExecute() {
+ var _this2 = this;
- next = next.nextSibling;
- }
+ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
- // We need to be sure to keep model in correct state after each small change, because converters
- // bases on that state and assumes that model is correct.
- // Because of that, if the command outdented items, we will outdent them starting from the last item, as
- // it is safer.
- if (_this2._indentBy < 0) {
- itemsToChange = itemsToChange.reverse();
- }
+ var document = this.editor.document;
+ var selection = document.selection;
+ var value = options.forceValue === undefined ? !this.value : options.forceValue;
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+ // If selection has non-collapsed ranges, we change attribute on nodes inside those ranges.
+ document.enqueueChanges(function () {
+ if (selection.isCollapsed) {
+ if (value) {
+ selection.setAttribute(_this2.attributeKey, true);
+ } else {
+ selection.removeAttribute(_this2.attributeKey);
+ }
+ } else {
+ var ranges = getSchemaValidRanges(_this2.attributeKey, selection.getRanges(), document.schema);
- try {
- for (var _iterator = itemsToChange[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var item = _step.value;
+ // Keep it as one undo step.
+ var batch = options.batch || document.batch();
- var indent = item.getAttribute('indent') + _this2._indentBy;
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- // If indent is lower than 0, it means that the item got outdented when it was not indented.
- // This means that we need to convert that list item to paragraph.
- if (indent < 0) {
- // To keep the model as correct as possible, first rename listItem, then remove attributes,
- // as listItem without attributes is very incorrect and will cause problems in converters.
- batch.rename(item, 'paragraph').removeAttribute(item, 'indent').removeAttribute(item, 'type');
- } else {
- // If indent is >= 0, just change the attribute value.
- batch.setAttribute(item, 'indent', indent);
- }
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
+ for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var range = _step.value;
+
+ if (value) {
+ batch.setAttribute(range, _this2.attributeKey, value);
+ } else {
+ batch.removeAttribute(range, _this2.attributeKey);
+ }
}
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
} finally {
- if (_didIteratorError) {
- throw _iteratorError;
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
}
}
}
});
}
-
- /**
- * @inheritDoc
- */
-
- }, {
- key: '_checkEnabled',
- value: function _checkEnabled() {
- // Check whether any of position's ancestor is a list item.
- var listItem = getClosestListItem(this.editor.document.selection.getFirstPosition());
-
- // If selection is not in a list item, the command is disabled.
- if (!listItem) {
- return false;
- }
-
- var prev = listItem.previousSibling;
- var oldIndent = listItem.getAttribute('indent');
- var newIndent = oldIndent + this._indentBy;
-
- if (this._indentBy > 0) {
- // If we are indenting, there are some conditions to meet.
- // Cannot indent first list item.
- if (!prev || prev.name != 'listItem') {
- return false;
- }
-
- // Indent can be at most greater by one than indent of previous item.
- if (prev.getAttribute('indent') + 1 < newIndent) {
- return false;
- }
- }
-
- // If we are outdenting it is enough to be in list item. Every list item can always be outdented.
- return true;
- }
}]);
- return IndentCommand;
+ return ToggleAttributeCommand;
}(Command);
/**
@@ -45175,625 +44149,595 @@ var IndentCommand = function (_Command) {
* For licensing, see LICENSE.md.
*/
+var BOLD = 'bold';
+
/**
- * View element class representing list item (``). It extends {@link engine.view.ContainerElement} and overwrites
- * {@link list.ViewListItemElement#getFillerOffset evaluating whether filler offset} is needed.
+ * The bold engine feature.
*
- * @memberOf list
- * @extends engine.view.ContainerElement
+ * It registers the `bold` command and introduces the `bold` attribute in the model which renders to the view
+ * as a `` element.
+ *
+ * @memberOf basic-styles
+ * @extends core.Feature
*/
-var ViewListItemElement = function (_ViewContainerElement) {
- inherits(ViewListItemElement, _ViewContainerElement);
+var BoldEngine = function (_Feature) {
+ inherits(BoldEngine, _Feature);
- /**
- * Creates `` view item.
- *
- * @param {Object|Iterable} [attrs] Collection of attributes.
- * @param {engine.view.Node|Iterable.} [children] List of nodes to be inserted into created element.
- */
- function ViewListItemElement(attrs, children) {
- classCallCheck(this, ViewListItemElement);
- return possibleConstructorReturn(this, Object.getPrototypeOf(ViewListItemElement).call(this, 'li', attrs, children));
+ function BoldEngine() {
+ classCallCheck(this, BoldEngine);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(BoldEngine).apply(this, arguments));
}
- /**
- * @inheritDoc
- */
+ createClass(BoldEngine, [{
+ key: 'init',
+ /**
+ * @inheritDoc
+ */
+ value: function init() {
+ var editor = this.editor;
+ var data = editor.data;
+ var editing = editor.editing;
- createClass(ViewListItemElement, [{
- key: 'getFillerOffset',
- value: function getFillerOffset() {
- var hasOnlyLists = !this.isEmpty && (this.getChild(0).name == 'ul' || this.getChild(0).name == 'ol');
+ // Allow bold attribute on all inline nodes.
+ editor.document.schema.allow({ name: '$inline', attributes: [BOLD] });
- return this.isEmpty || hasOnlyLists ? 0 : null;
+ // Build converter from model to view for data and editing pipelines.
+ buildModelConverter().for(data.modelToView, editing.modelToView).fromAttribute(BOLD).toElement('strong');
+
+ // Build converter from view to model for data pipeline.
+ buildViewConverter().for(data.viewToModel).fromElement('strong').fromElement('b').fromAttribute('style', { 'font-weight': 'bold' }).toAttribute(BOLD, true);
+
+ // Create bold command.
+ editor.commands.set(BOLD, new ToggleAttributeCommand(editor, BOLD));
}
}]);
- return ViewListItemElement;
-}(ContainerElement);
+ return BoldEngine;
+}(Feature);
/**
- * The list indent command. It is used by the {@link list.List list feature}.
- *
- * @memberOf list
- * @namespace list.converters
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
*/
-// Helper function that creates a `` structure out of given `modelItem` model `listItem` element.
-// Then, it binds created view list item (LI) with model `listItem` element.
-// The function then returns created view list item (LI).
-function generateLiInUl(modelItem, mapper) {
- var listType = modelItem.getAttribute('type') == 'numbered' ? 'ol' : 'ul';
- var viewItem = new ViewListItemElement();
-
- var viewList = new ContainerElement(listType, null);
- viewList.appendChildren(viewItem);
-
- mapper.bindElements(modelItem, viewItem);
-
- return viewItem;
-}
-
-// Helper function that seeks for a sibling of given `modelItem` that is a `listItem` element and meets given criteria.
-// `options` object may contain one or more of given values (by default they are `false`):
-// `options.getNext` - whether next or previous siblings should be checked (default = previous)
-// `options.checkAllSiblings` - whether all siblings or just the first one should be checked (default = only one),
-// `options.sameIndent` - whether sought sibling should have same indent (default = no),
-// `options.biggerIndent` - whether sought sibling should have bigger indent (default = no).
-// Either `options.sameIndent` or `options.biggerIndent` should be set to `true`.
-function getSiblingListItem(modelItem, options) {
- var direction = options.getNext ? 'nextSibling' : 'previousSibling';
- var checkAllSiblings = !!options.checkAllSiblings;
- var sameIndent = !!options.sameIndent;
- var biggerIndent = !!options.biggerIndent;
+/**
+ * The icon controller class.
+ *
+ * const model = new Model( {
+ * name: 'bold'
+ * } );
+ *
+ * // An instance of "bold" Icon.
+ * new Icon( model, new IconView() );
+ *
+ * See {@link ui.icon.IconView}, {@link ui.iconManager.IconManager}.
+ *
+ * @memberOf ui.icon
+ * @extends ui.Controller
+ */
- var indent = modelItem.getAttribute('indent');
+var Icon = function (_Controller) {
+ inherits(Icon, _Controller);
- var item = modelItem[direction];
+ /**
+ * Creates an instance of {@link ui.icon.Icon} class.
+ *
+ * @param {ui.icon.IconModel} model Model of this icon.
+ * @param {ui.View} view View of this icon.
+ */
+ function Icon(model, view) {
+ classCallCheck(this, Icon);
- while (item && item.name == 'listItem') {
- var itemIndent = item.getAttribute('indent');
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(Icon).call(this, model, view));
- if (sameIndent && indent == itemIndent || biggerIndent && indent < itemIndent) {
- return item;
- } else if (!checkAllSiblings || indent > itemIndent) {
- return null;
- }
+ view.bind('name').to(model);
+ return _this;
+ }
- item = item[direction];
- }
+ return Icon;
+}(Controller);
- return null;
-}
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
-// Helper function that takes two parameters, that are expected to be view list elements, and merges them.
-// The merge happen only if both parameters are UL or OL elements.
-function mergeViewLists(firstList, secondList) {
- if (firstList && secondList && (firstList.name == 'ul' || firstList.name == 'ol') && firstList.name == secondList.name) {
- viewWriter.mergeContainers(Position$1.createAfter(firstList));
- }
-}
+/**
+ * The icon view class.
+ *
+ * See {@link ui.icon.Icon}.
+ *
+ * @memberOf ui.icon
+ * @extends ui.View
+ */
-// Helper function that takes model list item element `modelItem`, corresponding view list item element `injectedItem`
-// that is not added to the view and is inside a view list element (`ul` or `ol`) and is that's list only child.
-// The list is inserted at correct position (element breaking may be needed) and then merged with it's siblings.
-// See comments below to better understand the algorithm.
-function injectViewList(modelItem, injectedItem, mapper) {
- var injectedList = injectedItem.parent;
+var IconView = function (_View) {
+ inherits(IconView, _View);
- // 1. Break after previous `listItem` if it has same or bigger indent.
- var prevModelItem = getSiblingListItem(modelItem, { sameIndent: true, biggerIndent: true });
+ /**
+ * @inheritDoc
+ */
+ function IconView() {
+ classCallCheck(this, IconView);
- if (prevModelItem) {
- var viewItem = mapper.toViewElement(prevModelItem);
- var viewPosition = Position$1.createAfter(viewItem);
- viewWriter.breakContainer(viewPosition);
- }
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(IconView).call(this));
- // 2. Break after closest previous `listItem` sibling with same indent.
- var sameIndentModelItem = getSiblingListItem(modelItem, { sameIndent: true, checkAllSiblings: true });
- // Position between broken lists will be a place where new list is inserted.
- // If there is nothing to break (`sameIndentModelItem` is falsy) it means that converted list item
- // is (will be) the first list item.
- var insertionPosition = void 0;
+ var bind = _this.bindTemplate;
- if (sameIndentModelItem) {
- var _viewItem = mapper.toViewElement(sameIndentModelItem);
- var _viewPosition = Position$1.createAfter(_viewItem);
- insertionPosition = viewWriter.breakContainer(_viewPosition);
- } else {
- // If there is a list item before converted list item, it means that that list item has lower indent.
- // In such case the created view list should be appended as a child of that item.
- var prevSibling = modelItem.previousSibling;
+ _this.template = new Template({
+ tag: 'svg',
+ ns: 'http://www.w3.org/2000/svg',
+ attributes: {
+ class: ['ck-icon']
+ },
+ children: [{
+ tag: 'use',
+ ns: 'http://www.w3.org/2000/svg',
+ attributes: {
+ href: {
+ ns: 'http://www.w3.org/1999/xlink',
+ value: bind.to('name', function (i) {
+ return '#ck-icon-' + i;
+ })
+ }
+ }
+ }]
+ });
- if (prevSibling && prevSibling.name == 'listItem') {
- insertionPosition = Position$1.createAt(mapper.toViewElement(prevSibling), 'end');
- } else {
- // This is the very first list item, use position mapping to get correct insertion position.
- insertionPosition = mapper.toViewPosition(Position.createBefore(modelItem));
- }
+ /**
+ * The name of the icon. It corresponds with the name of the
+ * file in the {@link ui.iconManager.IconManager}.
+ *
+ * @observable
+ * @member {String} ui.icon.IconView#name
+ */
+ return _this;
}
- // 3. Append new UL/OL in position after breaking in step 2.
- viewWriter.insert(insertionPosition, injectedList);
-
- // 4. If next sibling is list item with bigger indent, append it's UL/OL to new LI.
- var nextModelItem = getSiblingListItem(modelItem, { getNext: true, biggerIndent: true });
- var nextViewItem = mapper.toViewElement(nextModelItem);
-
- /* istanbul ignore if */ // Part of code connected with indenting that is not yet complete.
- if (nextViewItem) {
- var sourceRange = Range$2.createOn(nextViewItem.parent);
- var targetPosition = Position$1.createAt(injectedItem, 'end');
- viewWriter.move(sourceRange, targetPosition);
- }
+ return IconView;
+}(View);
- // 5. Merge new UL/OL with above and below items (ULs/OLs or LIs).
- mergeViewLists(injectedList, injectedList.nextSibling);
- mergeViewLists(injectedList.previousSibling, injectedList);
-}
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
/**
- * Model to view converter for `listItem` model element insertion.
+ * The button controller class. It uses {@link ui.icon.Icon} component
+ * to display an icon.
*
- * It creates `` (or ``) view structure out of `listItem` model element, inserts it at correct
- * position, and merges the list with surrounding lists (if able).
+ * const model = new Model( {
+ * label: 'Bold',
+ * isEnabled: true,
+ * isOn: false,
+ * icon: 'bold'
+ * } );
*
- * @see engine.conversion.ModelConversionDispatcher#event:insert
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Additional information about the change.
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
- * @param {Object} conversionApi Conversion interface.
+ * // An instance of Button with a label and an icon.
+ * new Button( model, new ButtonView() );
+ *
+ * See {@link ui.button.ButtonView}.
+ *
+ * @memberOf ui.button
+ * @extends ui.Controller
*/
-function modelViewInsertion(evt, data, consumable, conversionApi) {
- if (!consumable.test(data.item, 'insert') || !consumable.test(data.item, 'addAttribute:type') || !consumable.test(data.item, 'addAttribute:indent')) {
- return;
- }
- consumable.consume(data.item, 'insert');
- consumable.consume(data.item, 'addAttribute:type');
- consumable.consume(data.item, 'addAttribute:indent');
+var Button = function (_Controller) {
+ inherits(Button, _Controller);
- var modelItem = data.item;
- var viewItem = generateLiInUl(modelItem, conversionApi.mapper);
+ /**
+ * Creates an instance of {@link ui.button.Button} class.
+ *
+ * @param {ui.button.ButtonModel} model Model of this Button.
+ * @param {ui.View} view View of this Button.
+ */
+ function Button(model, view) {
+ classCallCheck(this, Button);
- injectViewList(modelItem, viewItem, conversionApi.mapper);
-}
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(Button).call(this, model, view));
-/**
- * Model to view converter for `type` attribute change on `listItem` model element.
- *
- * This change means that ``s parent changes from `` to `` (or vice versa). This is accomplished by breaking
- * view elements, changing their name and merging them.
- *
- * @see engine.conversion.ModelConversionDispatcher#event:changeAttribute
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Additional information about the change.
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
- * @param {Object} conversionApi Conversion interface.
- */
-function modelViewChangeType(evt, data, consumable, conversionApi) {
- if (!consumable.consume(data.item, 'changeAttribute:type')) {
- return;
- }
+ view.bind('label', 'isOn', 'isEnabled', 'withText', 'type').to(model);
+ view.bind('title').to(model, 'label', model, 'keystroke', function (label, keystroke) {
+ if (keystroke) {
+ label += ' (' + getEnvKeystrokeText(keystroke) + ')';
+ }
- var viewItem = conversionApi.mapper.toViewElement(data.item);
+ return label;
+ });
- // 1. Break the container after and before the list item.
- // This will create a view list with one view list item -- the one that changed type.
- viewWriter.breakContainer(Position$1.createBefore(viewItem));
- viewWriter.breakContainer(Position$1.createAfter(viewItem));
+ if (model.icon) {
+ view.bind('icon').to(model);
+ }
- // 2. Change name of the view list that holds the changed view item.
- // We cannot just change name property, because that would not render properly.
- var viewList = viewItem.parent;
- var listName = data.attributeNewValue == 'numbered' ? 'ol' : 'ul';
- viewList = viewWriter.rename(viewList, listName);
+ view.on('click', function () {
+ return model.fire('execute');
+ });
+ return _this;
+ }
- // 3. Merge the changed view list with other lists, if possible.
- mergeViewLists(viewList, viewList.nextSibling);
- mergeViewLists(viewList.previousSibling, viewList);
-}
+ /**
+ * @inheritDoc
+ */
-/**
- * Model to view converter for `listItem` model element remove.
- *
- * @see engine.conversion.ModelConversionDispatcher#event:remove
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Additional information about the change.
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
- * @param {Object} conversionApi Conversion interface.
- */
-function modelViewRemove(evt, data, consumable, conversionApi) {
- if (!consumable.consume(data.item, 'remove')) {
- return;
- }
- var viewItem = conversionApi.mapper.toViewElement(data.item);
+ createClass(Button, [{
+ key: 'init',
+ value: function init() {
+ if (this.model.icon) {
+ this.addCollection('children');
- // 1. Break the container after and before the list item.
- // This will create a view list with one view list item -- the one that changed type.
- viewWriter.breakContainer(Position$1.createBefore(viewItem));
- viewWriter.breakContainer(Position$1.createAfter(viewItem));
+ var iconModel = new Model();
+ iconModel.bind('name').to(this.model, 'icon');
- // 2. Remove the UL that contains just the removed LI.
- var viewList = viewItem.parent;
- viewWriter.remove(Range$2.createOn(viewList));
-}
+ this.add('children', new Icon(iconModel, new IconView()));
+ }
+
+ return get(Object.getPrototypeOf(Button.prototype), 'init', this).call(this);
+ }
+ }]);
+ return Button;
+}(Controller);
/**
- * Model to view converter for `listItem` model element move.
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * The button view class.
*
- * @see engine.conversion.ModelConversionDispatcher#event:move
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Additional information about the change.
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
- * @param {Object} conversionApi Conversion interface.
+ * See {@link ui.button.Button}.
+ *
+ * @memberOf ui.button
+ * @extends ui.View
*/
-function modelViewMove(evt, data, consumable, conversionApi) {
- if (!consumable.consume(data.item, 'move')) {
- return;
- }
- var viewItem = conversionApi.mapper.toViewElement(data.item);
+var ButtonView = function (_View) {
+ inherits(ButtonView, _View);
- // 1. Break the container after and before the list item.
- // This will create a view list with one view list item -- the one that changed type.
- viewWriter.breakContainer(Position$1.createBefore(viewItem));
- viewWriter.breakContainer(Position$1.createAfter(viewItem));
+ /**
+ * @inheritDoc
+ */
+ function ButtonView() {
+ classCallCheck(this, ButtonView);
- // 2. Extract view list with changed view list item and merge "hole" possibly created by breaking and removing elements.
- var viewList = viewItem.parent;
- var viewListPrev = viewList.previousSibling;
- var viewListNext = viewList.nextSibling;
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ButtonView).call(this));
- var insertionPosition = conversionApi.mapper.toViewPosition(data.targetPosition);
+ var bind = _this.bindTemplate;
- if (insertionPosition.parent.name == 'ol' || insertionPosition.parent.name == 'ul') {
- insertionPosition = viewWriter.breakContainer(insertionPosition);
- }
+ _this.template = new Template({
+ tag: 'button',
- viewWriter.move(Range$2.createOn(viewList), insertionPosition);
+ attributes: {
+ class: ['ck-button', bind.to('isEnabled', function (value) {
+ return value ? 'ck-enabled' : 'ck-disabled';
+ }), bind.to('isOn', function (value) {
+ return value ? 'ck-on' : 'ck-off';
+ }), bind.if('withText', 'ck-button_with-text')],
+ title: [bind.to('title')],
+ type: bind.to('type', function (value) {
+ return value ? value : 'button';
+ })
+ },
- // No worries, merging will happen only if both elements exist and they are same type of lists.
- mergeViewLists(viewListPrev, viewListNext);
- mergeViewLists(viewList, viewList.nextSibling);
- mergeViewLists(viewList.previousSibling, viewList);
-}
+ children: [{
+ tag: 'span',
-/**
- * Model to view converter for `indent` attribute change on `listItem` model element.
- *
- * @see engine.conversion.ModelConversionDispatcher#event:changeAttribute
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Additional information about the change.
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
- * @param {Object} conversionApi Conversion interface.
- */
-function modelViewChangeIndent(evt, data, consumable, conversionApi) {
- /* istanbul ignore if */ // Part of code connected with indenting that is not yet complete.
- if (!consumable.consume(data.item, 'changeAttribute:indent')) {
- return;
- }
+ attributes: {
+ class: ['ck-button__label']
+ },
- var viewItem = conversionApi.mapper.toViewElement(data.item);
+ children: [{
+ text: bind.to('label')
+ }]
+ }],
- // 1. Break the container after and before the list item.
- // This will create a view list with one view list item -- the one that changed type.
- viewWriter.breakContainer(Position$1.createBefore(viewItem));
- viewWriter.breakContainer(Position$1.createAfter(viewItem));
+ on: {
+ mousedown: bind.to(function (evt) {
+ evt.preventDefault();
+ }),
- // 2. Extract view list with changed view list item and merge "hole" possibly created by breaking and removing elements.
- var viewList = viewItem.parent;
- var viewListPrev = viewList.previousSibling;
+ click: bind.to(function (evt) {
+ // We can't make the button disabled using the disabled attribute, because it won't be focusable.
+ // Though, shouldn't this condition be moved to the button controller?
+ if (_this.isEnabled) {
+ _this.fire('click');
+ } else {
+ evt.preventDefault();
+ }
+ })
+ }
+ });
- viewWriter.remove(Range$2.createOn(viewList));
+ _this.register('children', function (el) {
+ return el;
+ });
- // If there is no `viewListPrev` it means that the first item was indented which is an error.
- mergeViewLists(viewListPrev, viewListPrev.nextSibling);
+ /**
+ * The label of the button view visible to the user.
+ *
+ * @observable
+ * @member {String} ui.button.ButtonView#label
+ */
- // 3. Inject view list like it is newly inserted.
- injectViewList(data.item, viewItem, conversionApi.mapper);
-}
+ /**
+ * The HTML type of the button. Default `button`.
+ *
+ * @observable
+ * @member {'button'|'submit'|'reset'|'menu'} ui.button.ButtonView#type
+ */
-/**
- * A special model to view converter introduced by {@link list.List List feature}. This converter is fired for
- * insert change of every model item, and should be fired before actual converter. The converter checks whether inserted
- * model item is a non-`listItem` element. If it is, and it is inserted inside a view list, the converter breaks the
- * list so the model element is inserted to the view parent element corresponding to its model parent element.
- *
- * The converter prevents such situations:
- *
- * // Model: // View:
- * foo
- *
- * // After change: // Correct view guaranteed by this converter:
- * foo xxx
- * xxx // Instead of this wrong view state:
- * bar
- *
- * @see engine.conversion.ModelConversionDispatcher#event:insert
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Additional information about the change.
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
- * @param {Object} conversionApi Conversion interface.
- */
-function modelViewSplitOnInsert(evt, data, consumable, conversionApi) {
- if (data.item.name != 'listItem') {
- var viewPosition = conversionApi.mapper.toViewPosition(data.range.start);
+ /**
+ * Controls whether the button view is "on", e.g. some feature which it represents
+ * is currently enabled.
+ *
+ * @observable
+ * @member {Boolean} ui.button.ButtonView#isOn
+ */
- // Break multiple ULs/OLs if there are.
- while (viewPosition.parent.name == 'ul' || viewPosition.parent.name == 'ol') {
- viewPosition = viewWriter.breakContainer(viewPosition);
+ /**
+ * Controls whether the button view is enabled (can be clicked).
+ *
+ * @observable
+ * @member {Boolean} ui.button.ButtonView#isEnabled
+ */
- /* istanbul ignore else */ // Part of code connected with indenting that is not yet complete.
- if (viewPosition.parent.parent === null) {
- break;
- }
+ /**
+ * (Optional) Whether the label of the button is hidden (e.g. button with icon only).
+ *
+ * @observable
+ * @member {Boolean} ui.button.ButtonView#withText
+ */
- /* istanbul ignore next */ // Part of code connected with indenting that is not yet complete.
- viewPosition = Position$1.createBefore(viewPosition.parent);
- }
+ /**
+ * (Optional) Title of the button displayed in the tooltip, i.e. when
+ * hovering the button with the mouse cursor.
+ *
+ * @observable
+ * @member {Boolean} ui.button.ButtonView#title
+ */
+ return _this;
}
-}
+
+ return ButtonView;
+}(View);
/**
- * A special model to view converter introduced by {@link list.List List feature}. This converter takes care of
- * merging view lists after something is removed or moved from near them.
- *
- * Example:
- *
- * // Model: // View:
- * foo
- * xxx xxx
- * bar
- *
- * // After change: // Correct view guaranteed by this converter:
- * foo
- *
- * @see engine.conversion.ModelConversionDispatcher#event:remove
- * @see engine.conversion.ModelConversionDispatcher#event:move
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Additional information about the change.
- * @param {engine.conversion.ModelConsumable} consumable Values to consume.
- * @param {Object} conversionApi Conversion interface.
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
*/
-function modelViewMergeAfter(evt, data, consumable, conversionApi) {
- var viewPosition = conversionApi.mapper.toViewPosition(data.sourcePosition);
- var viewItemPrev = viewPosition.nodeBefore;
- var viewItemNext = viewPosition.nodeAfter;
-
- // Merge lists if something (remove, move) was done from inside of list.
- // Merging will be done only if both items are view lists of the same type.
- // The check is done inside the helper function.
- mergeViewLists(viewItemPrev, viewItemNext);
-}
/**
- * View to model converter that converts view `` elements into `listItem` model elements.
+ * The bold feature. It introduces the Bold button and the Ctrl+B keystroke.
*
- * To set correct values of `type` and `indent` attribute the converter:
- * * checks ` `'s parent,
- * * passes `data.indent` value when ` `'s sub-items are converted.
+ * It uses the {@link basic-styles.BoldEngine bold engine feature}.
*
- * @see engine.conversion.ViewConversionDispatcher#event:element
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Object containing conversion input and a placeholder for conversion output and possibly other values.
- * @param {engine.conversion.ViewConsumable} consumable Values to consume.
- * @param {Object} conversionApi Conversion interface to be used by callback.
+ * @memberOf basic-styles
+ * @extends core.Feature
*/
-function viewModelConverter(evt, data, consumable, conversionApi) {
- if (consumable.consume(data.input, { name: true })) {
- // 1. Create `listItem` model element.
- var listItem = new Element('listItem');
- // 2. Handle `listItem` model element attributes.
- data.indent = data.indent ? data.indent : 0;
+var Bold = function (_Feature) {
+ inherits(Bold, _Feature);
- var type = data.input.parent.name == 'ul' ? 'bulleted' : 'numbered';
- listItem.setAttribute('type', type);
- listItem.setAttribute('indent', data.indent);
+ function Bold() {
+ classCallCheck(this, Bold);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Bold).apply(this, arguments));
+ }
- // 3. Handle ` ` children.
- data.context.push(listItem);
+ createClass(Bold, [{
+ key: 'init',
- // `listItem`s created recursievly should have bigger indent.
- data.indent++;
- // `listItem`s will be kept in flat structure.
- var items = [listItem];
+ /**
+ * @inheritDoc
+ */
+ value: function init() {
+ var editor = this.editor;
+ var t = editor.t;
+ var command = editor.commands.get('bold');
+ var keystroke = 'CTRL+B';
- // Check all children of the converted ` `.
- // At this point we assume there are no "whitespace" view text nodes in view list, between view list items.
- // This should be handled by `` and `` converters.
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+ // Create button model.
+ var buttonModel = new Model({
+ isEnabled: true,
+ isOn: false,
+ label: t('Bold'),
+ icon: 'bold',
+ keystroke: keystroke
+ });
- try {
- for (var _iterator = data.input.getChildren()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var child = _step.value;
+ // Bind button model to command.
+ buttonModel.bind('isOn', 'isEnabled').to(command, 'value', 'isEnabled');
- // Let's convert the child.
- var converted = conversionApi.convertItem(child, consumable, data);
+ // Execute command.
+ this.listenTo(buttonModel, 'execute', function () {
+ return editor.execute('bold');
+ });
- // If this is a view list element, we will convert it and concat the result (`listItem` model elements)
- // with already gathered results (in `items` array). `converted` should be a `ModelDocumentFragment`.
- if (child.name == 'ul' || child.name == 'ol') {
- items = items.concat(Array.from(converted.getChildren()));
- }
- // If it was not a list it was a "regular" list item content. Just append it to `listItem`.
- else {
- listItem.appendChildren(converted);
- }
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
+ // Add bold button to feature components.
+ editor.ui.featureComponents.add('bold', Button, ButtonView, buttonModel);
+
+ // Set the Ctrl+B keystroke.
+ editor.keystrokes.set(keystroke, 'bold');
+ }
+ }], [{
+ key: 'requires',
+
+ /**
+ * @inheritDoc
+ */
+ get: function get() {
+ return [BoldEngine];
}
+ }]);
+ return Bold;
+}(Feature);
- data.indent--;
- data.context.pop();
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- /* istanbul ignore next */ // Part of code connected with indenting that is not yet complete.
- data.output = data.output ? data.output.concat(items) : items;
- }
-}
+var ITALIC = 'italic';
/**
- * View to model converter for `` and `` view elements, that cleans the input view out of garbage.
- * This is mostly to clean white spaces from between `` view elements inside the view list element, however also
- * incorrect data can be cleared if the view was incorrect.
+ * The italic engine feature.
*
- * @see engine.conversion.ViewConversionDispatcher#event:element
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Object containing conversion input and a placeholder for conversion output and possibly other values.
- * @param {engine.conversion.ViewConsumable} consumable Values to consume.
+ * It registers the `italic` command and introduces the `italic` attribute in the model which renders to the view
+ * as an `` element.
+ *
+ * @memberOf basic-styles
+ * @extends core.Feature
*/
-function cleanList(evt, data, consumable) {
- if (consumable.test(data.input, { name: true })) {
- // Caching children because when we start removing them iterating fails.
- var children = Array.from(data.input.getChildren());
-
- var _iteratorNormalCompletion2 = true;
- var _didIteratorError2 = false;
- var _iteratorError2 = undefined;
- try {
- for (var _iterator2 = children[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- var child = _step2.value;
+var ItalicEngine = function (_Feature) {
+ inherits(ItalicEngine, _Feature);
- if (!child.name || child.name != 'li') {
- child.remove();
- }
- }
- } catch (err) {
- _didIteratorError2 = true;
- _iteratorError2 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion2 && _iterator2.return) {
- _iterator2.return();
- }
- } finally {
- if (_didIteratorError2) {
- throw _iteratorError2;
- }
- }
- }
+ function ItalicEngine() {
+ classCallCheck(this, ItalicEngine);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(ItalicEngine).apply(this, arguments));
}
-}
-/**
- * Callback for model position to view position mapping for {@link engine.conversion.Mapper}. The callback fixes positions
- * between `listItem` elements, that would be incorrectly mapped because of how list items are represented in model
- * and view.
- *
- * @see engine.conversion.Mapper#event:modelToViewPosition
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Object containing additional data and placeholder for mapping result.
- */
-function modelToViewPosition(evt, data) {
- var modelPosition = data.modelPosition;
- var mapper = data.mapper;
- var nodeAfter = modelPosition.nodeAfter;
+ createClass(ItalicEngine, [{
+ key: 'init',
- // `listItem` elements are mapped with view, so positions inside them will be correctly mapped by default algorithm.
- // Problem are positions between `listItem`s because they are incorrectly mapped to inside ``. This is
- // because of how view-to-model lengths work. What is important is that if a position is before a `listItem` and
- // it is not a first `listItem`, the position has to be placed before corresponding ` `. If this is the first
- // `listItem` position has to be before `` (this is default behavior).
- if (nodeAfter && nodeAfter.name == 'listItem') {
- var viewNode = mapper.toViewElement(nodeAfter);
+ /**
+ * @inheritDoc
+ */
+ value: function init() {
+ var editor = this.editor;
+ var data = editor.data;
+ var editing = editor.editing;
- if (viewNode && viewNode.index !== 0) {
- data.viewPosition = Position$1.createBefore(viewNode);
+ // Allow italic attribute on all inline nodes.
+ editor.document.schema.allow({ name: '$inline', attributes: [ITALIC] });
- evt.stop();
+ // Build converter from model to view for data and editing pipelines.
+ buildModelConverter().for(data.modelToView, editing.modelToView).fromAttribute(ITALIC).toElement('em');
+
+ // Build converter from view to model for data pipeline.
+ buildViewConverter().for(data.viewToModel).fromElement('em').fromElement('i').fromAttribute('style', { 'font-style': 'italic' }).toAttribute(ITALIC, true);
+
+ // Create italic command.
+ editor.commands.set(ITALIC, new ToggleAttributeCommand(editor, ITALIC));
}
- }
-}
+ }]);
+ return ItalicEngine;
+}(Feature);
/**
- * Callback for view position to model position mapping for {@link engine.conversion.Mapper}. The callback fixes positions
- * between `` elements, that would be incorrectly mapped because of how list items are represented in model
- * and view.
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * The italic feature. It introduces the Italic button and the Ctrl+I keystroke.
*
- * @see engine.conversion.Mapper#event:viewToModelPosition
- * @param {utils.EventInfo} evt Object containing information about the fired event.
- * @param {Object} data Object containing additional data and placeholder for mapping result.
+ * It uses the {@link basic-styles.ItalicEngine italic engine feature}.
+ *
+ * @memberOf basic-styles
+ * @extends core.Feature
*/
-function viewToModelPosition(evt, data) {
- var viewPosition = data.viewPosition;
- var mapper = data.mapper;
- var nodeAfter = viewPosition.nodeAfter;
- var nodeBefore = viewPosition.nodeBefore;
- var modelNode = void 0;
+var Italic = function (_Feature) {
+ inherits(Italic, _Feature);
- if (nodeAfter) {
- if (nodeAfter.name == 'ul' || nodeAfter.name == 'ol') {
- // If the position is before view list, model position should be placed before `listItem`
- // that is bound to the first ` ` of that view list.
- // Default algorithm would work like this but only for top-level list.
- modelNode = mapper.toModelElement(nodeAfter.getChild(0));
- } else if (nodeAfter.name == 'li') {
- // If the position is before view list item, just place model position before bound `listItem` element.
- modelNode = mapper.toModelElement(nodeAfter);
- }
+ function Italic() {
+ classCallCheck(this, Italic);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Italic).apply(this, arguments));
+ }
- if (modelNode) {
- data.modelPosition = Position.createBefore(modelNode);
+ createClass(Italic, [{
+ key: 'init',
+
+
+ /**
+ * @inheritDoc
+ */
+ value: function init() {
+ var editor = this.editor;
+ var t = editor.t;
+ var command = editor.commands.get('italic');
+ var keystroke = 'CTRL+I';
+
+ // Create button model.
+ var buttonModel = new Model({
+ isEnabled: true,
+ isOn: false,
+ label: t('Italic'),
+ icon: 'italic',
+ keystroke: keystroke
+ });
+
+ // Bind button model to command.
+ buttonModel.bind('isOn', 'isEnabled').to(command, 'value', 'isEnabled');
+
+ // Execute command.
+ this.listenTo(buttonModel, 'execute', function () {
+ return editor.execute('italic');
+ });
+
+ // Add bold button to feature components.
+ editor.ui.featureComponents.add('italic', Button, ButtonView, buttonModel);
+
+ // Set the Ctrl+I keystroke.
+ editor.keystrokes.set(keystroke, 'italic');
}
- } else if (nodeBefore) {
- var viewNode = void 0;
+ }], [{
+ key: 'requires',
- // Find ` ` after which we want to place position.
- // We want to find a ` ` that will be mapped to model `listItem` element. That `listItem` will
- // be used as a reference point to evaluate model position.
- /* istanbul ignore if */ // Part of code connected with indenting that is not yet complete.
- if (nodeBefore.name == 'ul' || nodeBefore.name == 'ol') {
- // If the position is before view list, take the last ` ` of that view list.
- viewNode = nodeBefore.getChild(nodeBefore.childCount - 1);
- } else if (nodeBefore.name == 'li') {
- // If the position is before view list item, take that ` `.
- viewNode = nodeBefore;
+ /**
+ * @inheritDoc
+ */
+ get: function get() {
+ return [ItalicEngine];
}
+ }]);
+ return Italic;
+}(Feature);
- // Evaluate correct model position.
- // At this stage we have a ` `. This ` ` may have nested ` `s inside. We will use `mapper`
- // to obtain this ` `'s model length. Placing model position after that ` ` will be done
- // by placing it before the bound `listItem` and moving by offset equal to ` `s length.
- if (viewNode) {
- modelNode = mapper.toModelElement(viewNode);
- var offset = mapper.getModelLength(viewNode);
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- data.modelPosition = Position.createBefore(modelNode).getShiftedBy(offset);
- }
- }
+/**
+ * Facade over the native [`DataTransfer`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer) object.
+ *
+ * @memberOf clipboard
+ */
+var DataTransfer = function () {
+ function DataTransfer(nativeDataTransfer) {
+ classCallCheck(this, DataTransfer);
- // If we found a model position, stop the event.
- if (data.modelPosition !== null) {
- evt.stop();
+ /**
+ * The native DataTransfer object.
+ *
+ * @private
+ * @member {DataTransfer} clipboard.DataTransfer#_native
+ */
+ this._native = nativeDataTransfer;
}
-}
+
+ /**
+ * Gets data from the data transfer by its mime type.
+ *
+ * dataTransfer.getData( 'text/plain' );
+ *
+ * @param {String} type The mime type. E.g. `text/html` or `text/plain`.
+ * @returns {String}
+ */
+
+
+ createClass(DataTransfer, [{
+ key: "getData",
+ value: function getData(type) {
+ return this._native.getData(type);
+ }
+ }]);
+ return DataTransfer;
+}();
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -45801,86 +44745,96 @@ function viewToModelPosition(evt, data) {
*/
/**
- * The engine of the lists feature. It handles creating, editing and removing lists and list items.
- * It registers the `numberedList`, `bulletedList`, `indentList` and `outdentList` commands.
+ * {@link engine.view.Document#paste Paste} event observer.
*
- * @memberOf list
- * @extends core.Feature
+ * Note that this observer is not available by default. To make it available it needs to be added to {@link engine.view.Document}
+ * by the {@link engine.view.Document#addObserver} method.
+ *
+ * @memberOf clipboard
+ * @extends engine.view.observer.DomEventObserver
*/
-var ListEngine = function (_Feature) {
- inherits(ListEngine, _Feature);
-
- function ListEngine() {
- classCallCheck(this, ListEngine);
- return possibleConstructorReturn(this, Object.getPrototypeOf(ListEngine).apply(this, arguments));
- }
-
- createClass(ListEngine, [{
- key: 'init',
+var ClipboardObserver = function (_DomEventObserver) {
+ inherits(ClipboardObserver, _DomEventObserver);
- /**
- * @inheritDoc
- */
- value: function init() {
- var editor = this.editor;
+ function ClipboardObserver(doc) {
+ classCallCheck(this, ClipboardObserver);
- // Schema.
- var schema = editor.document.schema;
- schema.registerItem('listItem', '$block');
- schema.allow({
- name: 'listItem',
- inside: '$root',
- attributes: ['type', 'indent']
- });
- schema.requireAttributes('listItem', ['type', 'indent']);
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ClipboardObserver).call(this, doc));
- // Converters.
- var data = editor.data;
- var editing = editor.editing;
+ _this.domEventType = 'paste';
+ return _this;
+ }
- editing.mapper.on('modelToViewPosition', modelToViewPosition);
- editing.mapper.on('viewToModelPosition', viewToModelPosition);
- data.mapper.on('modelToViewPosition', modelToViewPosition);
+ createClass(ClipboardObserver, [{
+ key: 'onDomEvent',
+ value: function onDomEvent(domEvent) {
+ this.fire(domEvent.type, domEvent, {
+ dataTransfer: new DataTransfer(domEvent.clipboardData)
+ });
+ }
+ }]);
+ return ClipboardObserver;
+}(DomEventObserver);
- editing.modelToView.on('insert', modelViewSplitOnInsert, { priority: 'high' });
- editing.modelToView.on('insert:listItem', modelViewInsertion);
- data.modelToView.on('insert', modelViewSplitOnInsert, { priority: 'high' });
- data.modelToView.on('insert:listItem', modelViewInsertion);
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- // Only change converter is needed. List item's type attribute is required, so it's adding is handled when
- // list item is added and you cannot remove it.
- editing.modelToView.on('changeAttribute:type:listItem', modelViewChangeType);
- data.modelToView.on('changeAttribute:type:listItem', modelViewChangeType);
+/**
+ * Converts plain text to its HTML-ized version.
+ *
+ * @function clipboard.utils.plainTextToHtml
+ * @param {String} text The plain text to convert.
+ * @returns {String} HTML generated from the plain text.
+ */
+function plainTextToHtml(text) {
+ text = text
+ // Encode <>.
+ .replace(//g, '>')
+ // Creates paragraphs for double line breaks and change single line breaks to spaces.
+ // In the future single line breaks may be converted into s.
+ .replace(/\n\n/g, '
').replace(/\n/g, ' ')
+ // Preserve trailing spaces (only the first and last one – the rest is handled below).
+ .replace(/^\s/, ' ').replace(/\s$/, ' ')
+ // Preserve other subsequent spaces now.
+ .replace(/\s\s/g, ' ');
- editing.modelToView.on('remove:listItem', modelViewRemove);
- editing.modelToView.on('remove', modelViewMergeAfter, { priority: 'low' });
- data.modelToView.on('remove:listItem', modelViewRemove);
- data.modelToView.on('remove', modelViewMergeAfter, { priority: 'low' });
+ if (text.indexOf('
') > -1) {
+ // If we created paragraphs above, add the trailing ones.
+ text = '
' + text + '
';
+ }
- editing.modelToView.on('move:listItem', modelViewMove);
- editing.modelToView.on('move', modelViewMergeAfter, { priority: 'low' });
- data.modelToView.on('move:listItem', modelViewMove);
- data.modelToView.on('move', modelViewMergeAfter, { priority: 'low' });
+ // TODO:
+ // * What about '\nfoo' vs ' foo'?
- editing.modelToView.on('changeAttribute:indent:listItem', modelViewChangeIndent);
- data.modelToView.on('changeAttribute:indent:listItem', modelViewChangeIndent);
+ return text;
+}
- data.viewToModel.on('element:li', viewModelConverter);
- data.viewToModel.on('element:ul', cleanList, { priority: 'high' });
- data.viewToModel.on('element:ol', cleanList, { priority: 'high' });
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- // Register commands for numbered and bulleted list.
- editor.commands.set('numberedList', new ListCommand(editor, 'numbered'));
- editor.commands.set('bulletedList', new ListCommand(editor, 'bulleted'));
+/**
+ * Removes some popular browser quirks out of the clipboard data (HTML).
+ *
+ * @function clipboard.utils.normalizeClipboardData
+ * @param {String} data The HTML data to normalize.
+ * @returns {String} Normalized HTML.
+ */
+function normalizeClipboardData(data) {
+ return data.replace(/(\s+)<\/span>/g, function (fullMatch, spaces) {
+ // Handle the most popular and problematic case when even a single space becomes an nbsp;.
+ // Decode those to normal spaces. Read more in https://github.com/ckeditor/ckeditor5-clipboard/issues/2.
+ if (spaces.length == 1) {
+ return ' ';
+ }
- // Register commands for indenting.
- editor.commands.set('indentList', new IndentCommand(editor, 'forward'));
- editor.commands.set('outdentList', new IndentCommand(editor, 'backward'));
- }
- }]);
- return ListEngine;
-}(Feature);
+ return spaces;
+ });
+}
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -45888,68 +44842,121 @@ var ListEngine = function (_Feature) {
*/
/**
- * Checks {@link engine.model.Document#schema} if attribute is allowed in selection:
- * * if selection is on range, the command is enabled if any of nodes in that range can have bold,
- * * if selection is collapsed, the command is enabled if text with bold is allowed in that node.
+ * The clipboard feature. Currently, it's only responsible for intercepting the `paste` event and
+ * passing the pasted content through the clipboard pipeline.
*
- * @param {String} attribute Attribute key.
- * @param {engine.model.Selection} selection Selection which ranges will be validate.
- * @param {engine.model.Schema} schema Document schema.
- * @returns {Boolean}
+ * ## Clipboard Pipeline
+ *
+ * The feature creates the clipboard pipeline which allows for processing clipboard content
+ * before it gets inserted into the editor. The pipeline consists of two events on which
+ * the features can listen in order to modify or totally override the default behavior.
+ *
+ * ### On {@link engine.view.Document#paste}
+ *
+ * The default action is to:
+ *
+ * 1. get HTML or plain text from the clipboard,
+ * 2. fire {@link engine.view.Document#clipboardInput} with the clipboard data parsed to
+ * a {@link engine.view.DocumentFragment view document fragment},
+ * 3. prevent the default action of the native `paste` event.
+ *
+ * This action is performed by a low priority listener, so it can be overridden by a normal one.
+ * You'd only need to do this when a deeper change in pasting behavior was needed. For example,
+ * a feature which wants to differently read data from the clipboard (the {@link clipboard.DataTransfer `DataTransfer`}).
+ * should plug a listener at this stage.
+ *
+ * ### On {@link engine.view.Document#clipboardInput}
+ *
+ * The default action is to insert the content (`data.content`, represented by a {@link engine.view.DocumentFragment})
+ * to an editor if the data is not empty.
+ *
+ * This action is performed by a low priority listener, so it can be overridden by a normal one.
+ *
+ * At this stage the pasted content can be processed by the features. E.g. a feature which wants to transform
+ * a pasted text into a link can be implemented in this way:
+ *
+ * this.listenTo( editor.editing.view, 'clipboardInput', ( evt, data ) => {
+ * if ( data.content.childCount == 1 && isUrlText( data.content.getChild( 0 ) ) ) {
+ * const linkUrl = data.content.getChild( 0 ).data;
+ *
+ * data.content = new ViewDocumentFragment( [
+ * ViewElement(
+ * 'a',
+ * { href: linkUrl },
+ * [ new ViewText( linkUrl ) ]
+ * )
+ * ] );
+ * }
+ * } );
+ *
+ * @memberOf clipboard
+ * @extends core.Feature
*/
-function isAttributeAllowedInSelection(attribute, selection, schema) {
- if (selection.isCollapsed) {
- // Check whether schema allows for a test with `attributeKey` in caret position.
- return schema.check({ name: '$text', inside: selection.getFirstPosition(), attributes: attribute });
- } else {
- var ranges = selection.getRanges();
- // For all ranges, check nodes in them until you find a node that is allowed to have `attributeKey` attribute.
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+var Clipboard = function (_Feature) {
+ inherits(Clipboard, _Feature);
- try {
- for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var range = _step.value;
+ function Clipboard() {
+ classCallCheck(this, Clipboard);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Clipboard).apply(this, arguments));
+ }
- var walker = new TreeWalker({ boundaries: range, mergeCharacters: true });
- var last = walker.position;
- var step = walker.next();
+ createClass(Clipboard, [{
+ key: 'init',
- // Walk the range.
- while (!step.done) {
- // If returned item does not have name property, it is a model.TextFragment.
- var name = step.value.item.name || '$text';
+ /**
+ * @inheritDoc
+ */
+ value: function init() {
+ var _this2 = this;
+
+ var editor = this.editor;
+ var editingView = editor.editing.view;
+
+ /**
+ * Data processor used to convert pasted HTML to a view structure.
+ *
+ * @private
+ * @member {engine.dataProcessor.HtmlDataProcessor} clipboard.Clipboard#_htmlDataProcessor
+ */
+ this._htmlDataProcessor = new HtmlDataProcessor();
+
+ editingView.addObserver(ClipboardObserver);
+
+ // The clipboard pipeline.
+
+ this.listenTo(editingView, 'paste', function (evt, data) {
+ var dataTransfer = data.dataTransfer;
+ var content = '';
+
+ if (dataTransfer.getData('text/html')) {
+ content = normalizeClipboardData(dataTransfer.getData('text/html'));
+ } else if (dataTransfer.getData('text/plain')) {
+ content = plainTextToHtml(dataTransfer.getData('text/plain'));
+ }
+
+ content = _this2._htmlDataProcessor.toView(content);
+
+ data.preventDefault();
- if (schema.check({ name: name, inside: last, attributes: attribute })) {
- // If we found a node that is allowed to have the attribute, return true.
- return true;
- }
+ editingView.fire('clipboardInput', { dataTransfer: dataTransfer, content: content });
+ }, { priority: 'low' });
- last = walker.position;
- step = walker.next();
- }
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
- }
+ this.listenTo(editingView, 'clipboardInput', function (evt, data) {
+ if (!data.content.isEmpty) {
+ (function () {
+ var doc = editor.document;
- // If we haven't found such node, return false.
- return false;
-}
+ doc.enqueueChanges(function () {
+ _this2.editor.data.insert(data.content, doc.selection);
+ });
+ })();
+ }
+ }, { priority: 'low' });
+ }
+ }]);
+ return Clipboard;
+}(Feature);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -45957,252 +44964,166 @@ function isAttributeAllowedInSelection(attribute, selection, schema) {
*/
/**
- * An extension of basic {@link core.command.Command} class, which provides utilities for a command that toggle a single
- * attribute on a text or element with value `true`. ToggleAttributeCommand uses {@link engine.model.Document#selection}
- * to decide which nodes (if any) should be changed, and applies or removes attributes from them.
- * See {@link engine.view.Converter#execute} for more.
- *
- * The command checks {@link engine.model.Document#schema} to decide if it should be enabled.
- * See {@link engine.view.Converter#checkSchema} for more.
+ * Enter command. It is used by the {@link enter.Enter Enter feature} to handle the Enter key.
*
- * @memberOf core.command
+ * @member enter
+ * @extends core.command.Command
*/
-var ToggleAttributeCommand = function (_Command) {
- inherits(ToggleAttributeCommand, _Command);
-
- /**
- * @see core.command.Command
- * @param {core.editor.Editor} editor
- * @param {String} attributeKey Attribute that will be set by the command.
- */
- function ToggleAttributeCommand(editor, attributeKey) {
- classCallCheck(this, ToggleAttributeCommand);
-
- /**
- * Attribute that will be set by the command.
- *
- * @member {String} core.command.ToggleAttributeCommand#attributeKey
- */
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ToggleAttributeCommand).call(this, editor));
-
- _this.attributeKey = attributeKey;
-
- /**
- * Flag indicating whether command is active. For collapsed selection it means that typed characters will have
- * the command's attribute set. For range selection it means that all nodes inside have the attribute applied.
- *
- * @observable
- * @member {Boolean} core.command.ToggleAttributeCommand#value
- */
- _this.set('value', false);
+var EnterCommand = function (_Command) {
+ inherits(EnterCommand, _Command);
- _this.listenTo(_this.editor.document.selection, 'change:attribute', function () {
- _this.value = _this.editor.document.selection.hasAttribute(_this.attributeKey);
- });
- return _this;
+ function EnterCommand() {
+ classCallCheck(this, EnterCommand);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(EnterCommand).apply(this, arguments));
}
- /**
- * Checks if {@link engine.model.Document#schema} allows to create attribute in {@link engine.model.Document#selection}
- *
- * @private
- * @returns {Boolean}
- */
-
-
- createClass(ToggleAttributeCommand, [{
- key: '_checkEnabled',
- value: function _checkEnabled() {
- var document = this.editor.document;
-
- return isAttributeAllowedInSelection(this.attributeKey, document.selection, document.schema);
- }
+ createClass(EnterCommand, [{
+ key: '_doExecute',
/**
- * Executes the command: adds or removes attributes to nodes or selection.
- *
- * If the command is active (`value == true`), it will remove attributes. Otherwise, it will set attributes.
- *
- * The execution result differs, depending on the {@link engine.model.Document#selection}:
- * * if selection is on a range, the command applies the attribute on all nodes in that ranges
- * (if they are allowed to have this attribute by the {@link engine.model.Schema schema}),
- * * if selection is collapsed in non-empty node, the command applies attribute to the {@link engine.model.Document#selection}
- * itself (note that typed characters copy attributes from selection),
- * * if selection is collapsed in empty node, the command applies attribute to the parent node of selection (note
- * that selection inherits all attributes from a node if it is in empty node).
- *
- * If the command is disabled (`isEnabled == false`) when it is executed, nothing will happen.
- *
- * @private
- * @param {Object} [options] Options of command.
- * @param {Boolean} [options.forceValue] If set it will force command behavior. If `true`, command will apply attribute,
- * otherwise command will remove attribute. If not set, command will look for it's current value to decide what it should do.
- * @param {engine.model.Batch} [options.batch] Batch to group undo steps.
+ * @inheritDoc
*/
-
- }, {
- key: '_doExecute',
value: function _doExecute() {
var _this2 = this;
- var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
+ var doc = this.editor.document;
+ var batch = doc.batch();
- var document = this.editor.document;
- var selection = document.selection;
- var value = options.forceValue === undefined ? !this.value : options.forceValue;
+ doc.enqueueChanges(function () {
+ enterBlock(batch, doc.selection);
- // If selection has non-collapsed ranges, we change attribute on nodes inside those ranges.
- document.enqueueChanges(function () {
- if (selection.isCollapsed) {
- if (value) {
- selection.setAttribute(_this2.attributeKey, true);
- } else {
- selection.removeAttribute(_this2.attributeKey);
- }
- } else {
- var ranges = getSchemaValidRanges(_this2.attributeKey, selection.getRanges(), document.schema);
+ _this2.fire('afterExecute', { batch: batch });
+ });
+ }
+ }]);
+ return EnterCommand;
+}(Command);
- // Keep it as one undo step.
- var batch = options.batch || document.batch();
+function enterBlock(batch, selection) {
+ var doc = batch.document;
+ var isSelectionEmpty = selection.isCollapsed;
+ var range = selection.getFirstRange();
+ var startElement = range.start.parent;
+ var endElement = range.end.parent;
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+ // Don't touch the root.
+ if (startElement.root == startElement) {
+ if (!isSelectionEmpty) {
+ doc.composer.deleteContents(batch, selection);
+ }
- try {
- for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var range = _step.value;
+ return;
+ }
- if (value) {
- batch.setAttribute(range, _this2.attributeKey, value);
- } else {
- batch.removeAttribute(range, _this2.attributeKey);
- }
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
+ if (isSelectionEmpty) {
+ splitBlock(batch, selection, range.start);
+ } else {
+ var shouldMerge = range.start.isAtStart && range.end.isAtEnd;
+ var isContainedWithinOneElement = startElement == endElement;
+
+ doc.composer.deleteContents(batch, selection, { merge: shouldMerge });
+
+ if (!shouldMerge) {
+ // Partially selected elements.
+ //
+ // x[xx]x -> x^x -> x ^x
+ if (isContainedWithinOneElement) {
+ splitBlock(batch, selection, selection.focus);
+ }
+ // Selection over multiple elements.
+ //
+ // x[x y]y
-> x^
y
-> x ^y
+ else {
+ selection.collapse(endElement);
}
- });
}
- }]);
- return ToggleAttributeCommand;
-}(Command);
+ }
+}
+
+function splitBlock(batch, selection, splitPos) {
+ batch.split(splitPos);
+ selection.collapse(splitPos.parent.nextSibling);
+}
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
-var BOLD = 'bold';
-
/**
- * The bold engine feature.
- *
- * It registers the `bold` command and introduces the `bold` attribute in the model which renders to the view
- * as a `` element.
+ * Enter observer introduces the {@link engine.view.Document#enter} event.
*
- * @memberOf basic-styles
- * @extends core.Feature
+ * @memberOf enter
+ * @extends engine.view.observer.Observer
*/
-var BoldEngine = function (_Feature) {
- inherits(BoldEngine, _Feature);
+var EnterObserver = function (_Observer) {
+ inherits(EnterObserver, _Observer);
- function BoldEngine() {
- classCallCheck(this, BoldEngine);
- return possibleConstructorReturn(this, Object.getPrototypeOf(BoldEngine).apply(this, arguments));
- }
+ function EnterObserver(document) {
+ classCallCheck(this, EnterObserver);
- createClass(BoldEngine, [{
- key: 'init',
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(EnterObserver).call(this, document));
+
+ document.on('keydown', function (evt, data) {
+ if (_this.isEnabled && data.keyCode == keyCodes.enter) {
+ document.fire('enter', new DomEventData(document, data.domEvent));
+ }
+ });
+ return _this;
+ }
- /**
+ /**
* @inheritDoc
*/
- value: function init() {
- var editor = this.editor;
- var data = editor.data;
- var editing = editor.editing;
-
- // Allow bold attribute on all inline nodes.
- editor.document.schema.allow({ name: '$inline', attributes: [BOLD] });
- // Build converter from model to view for data and editing pipelines.
- buildModelConverter().for(data.modelToView, editing.modelToView).fromAttribute(BOLD).toElement('strong');
-
- // Build converter from view to model for data pipeline.
- buildViewConverter().for(data.viewToModel).fromElement('strong').fromElement('b').fromAttribute('style', { 'font-weight': 'bold' }).toAttribute(BOLD, true);
- // Create bold command.
- editor.commands.set(BOLD, new ToggleAttributeCommand(editor, BOLD));
- }
- }]);
- return BoldEngine;
-}(Feature);
+ createClass(EnterObserver, [{
+ key: 'observe',
+ value: function observe() {}
+ }]);
+ return EnterObserver;
+}(Observer);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
-var ITALIC = 'italic';
-
/**
- * The italic engine feature.
- *
- * It registers the `italic` command and introduces the `italic` attribute in the model which renders to the view
- * as an `` element.
+ * The Enter feature. Handles the Enter and Shift + Enter keys in the editor.
*
- * @memberOf basic-styles
+ * @memberOf enter
* @extends core.Feature
*/
-var ItalicEngine = function (_Feature) {
- inherits(ItalicEngine, _Feature);
+var Enter = function (_Feature) {
+ inherits(Enter, _Feature);
- function ItalicEngine() {
- classCallCheck(this, ItalicEngine);
- return possibleConstructorReturn(this, Object.getPrototypeOf(ItalicEngine).apply(this, arguments));
+ function Enter() {
+ classCallCheck(this, Enter);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Enter).apply(this, arguments));
}
- createClass(ItalicEngine, [{
+ createClass(Enter, [{
key: 'init',
-
- /**
- * @inheritDoc
- */
value: function init() {
var editor = this.editor;
- var data = editor.data;
- var editing = editor.editing;
-
- // Allow italic attribute on all inline nodes.
- editor.document.schema.allow({ name: '$inline', attributes: [ITALIC] });
+ var editingView = editor.editing.view;
- // Build converter from model to view for data and editing pipelines.
- buildModelConverter().for(data.modelToView, editing.modelToView).fromAttribute(ITALIC).toElement('em');
+ editingView.addObserver(EnterObserver);
- // Build converter from view to model for data pipeline.
- buildViewConverter().for(data.viewToModel).fromElement('em').fromElement('i').fromAttribute('style', { 'font-style': 'italic' }).toAttribute(ITALIC, true);
+ editor.commands.set('enter', new EnterCommand(editor));
- // Create italic command.
- editor.commands.set(ITALIC, new ToggleAttributeCommand(editor, ITALIC));
+ // TODO We may use the keystroke handler for that.
+ this.listenTo(editingView, 'enter', function (evt, data) {
+ editor.execute('enter');
+ data.preventDefault();
+ }, { priority: 'low' });
}
}]);
- return ItalicEngine;
+ return Enter;
}(Feature);
/**
@@ -46211,138 +45132,43 @@ var ItalicEngine = function (_Feature) {
*/
/**
- * Includes a set of predefined autoformatting actions.
- *
- * ## Bulleted list
- *
- * You can create a bulleted list by staring a line with:
- *
- * * `* `
- * * `- `
- *
- * ## Numbered list
- *
- * You can create a numbered list by staring a line with:
- *
- * * `1. `
- * * `1) `
- *
- * ## Headings
- *
- * You can create a heading by starting a line with:
- *
- * `# ` – will create Heading 1,
- * `## ` – will create Heading 2,
- * `### ` – will create Heading 3.
- *
- * ## Bold and italic
- *
- * You can apply bold or italic to a text by typing Markdown formatting:
- *
- * * `**foo bar**` or `__foo bar__` – will bold the text,
- * * `*foo bar*` or `_foo bar_` – will italicize the text,
+ * The paragraph feature for the editor.
+ * Introduces the `` element in the model which renders as a `` element in the DOM and data.
*
- * @memberOf autoformat
+ * @memberOf paragraph
* @extends core.Feature
*/
-var Autoformat = function (_Feature) {
- inherits(Autoformat, _Feature);
+var Paragraph = function (_Feature) {
+ inherits(Paragraph, _Feature);
- function Autoformat() {
- classCallCheck(this, Autoformat);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Autoformat).apply(this, arguments));
+ function Paragraph() {
+ classCallCheck(this, Paragraph);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Paragraph).apply(this, arguments));
}
- createClass(Autoformat, [{
+ createClass(Paragraph, [{
key: 'init',
-
/**
* @inheritDoc
*/
value: function init() {
- this._addListAutoformats();
- this._addHeadingAutoformats();
- this._addInlineAutoformats();
- }
-
- /**
- * Adds autoformatting related to ListEngine commands.
- *
- * When typed:
- * - `* ` or `- ` - paragraph will be changed to a bulleted list,
- * - `1. ` or `1) ` - paragraph will be changed to a numbered list (1 can be any digit or list of digits).
- *
- * @private
- */
-
- }, {
- key: '_addListAutoformats',
- value: function _addListAutoformats() {
- new BlockAutoformatEngine(this.editor, /^[\*\-]\s$/, 'bulletedList');
- new BlockAutoformatEngine(this.editor, /^\d+[\.|)]?\s$/, 'numberedList');
- }
-
- /**
- * Adds autoformatting related to HeadingEngine commands.
- * When typed `# ` or `## ` or `### ` paragraph will be changed to a corresponding heading level.
- *
- * @private
- */
-
- }, {
- key: '_addHeadingAutoformats',
- value: function _addHeadingAutoformats() {
- var _this2 = this;
-
- new BlockAutoformatEngine(this.editor, /^(#{1,3})\s$/, function (context) {
- var batch = context.batch;
- var match = context.match;
-
- var headingLevel = match[1].length;
-
- _this2.editor.execute('heading', {
- batch: batch,
- formatId: 'heading' + headingLevel
- });
- });
- }
-
- /**
- * Adds inline autoformatting capabilities to the editor.
- *
- * When typed:
- * - `**foobar**`: `**` characters are removed, and `foobar` is set to bold,
- * - `__foobar__`: `__` characters are removed, and `foobar` is set to bold,
- * - `*foobar*`: `*` characters are removed, and `foobar` is set to italic,
- * - `_foobar_`: `_` characters are removed, and `foobar` is set to italic.
- *
- * @private
- */
+ var editor = this.editor;
+ var data = editor.data;
+ var editing = editor.editing;
- }, {
- key: '_addInlineAutoformats',
- value: function _addInlineAutoformats() {
- new InlineAutoformatEngine(this.editor, /(\*\*)([^\*]+)(\*\*)$/g, 'bold');
- new InlineAutoformatEngine(this.editor, /(__)([^_]+)(__)$/g, 'bold');
+ // Schema.
+ editor.document.schema.registerItem('paragraph', '$block');
- // The italic autoformatter cannot be triggered by the bold markers, so we need to check the
- // text before the pattern (e.g. `(?:^|[^\*])`).
- new InlineAutoformatEngine(this.editor, /(?:^|[^\*])(\*)([^\*_]+)(\*)$/g, 'italic');
- new InlineAutoformatEngine(this.editor, /(?:^|[^_])(_)([^_]+)(_)$/g, 'italic');
- }
- }], [{
- key: 'requires',
+ // Build converter from model to view for data and editing pipelines.
+ buildModelConverter().for(data.modelToView, editing.modelToView).fromElement('paragraph').toElement('p');
- /**
- * @inheritDoc
- */
- get: function get() {
- return [HeadingEngine, ListEngine, BoldEngine, ItalicEngine];
+ // Build converter from view to model for data pipeline.
+ buildViewConverter().for(data.viewToModel).fromElement('p').toElement('paragraph');
}
}]);
- return Autoformat;
+ return Paragraph;
}(Feature);
/**
@@ -46351,306 +45177,340 @@ var Autoformat = function (_Feature) {
*/
/**
- * The icon controller class.
- *
- * const model = new Model( {
- * name: 'bold'
- * } );
- *
- * // An instance of "bold" Icon.
- * new Icon( model, new IconView() );
- *
- * See {@link ui.icon.IconView}, {@link ui.iconManager.IconManager}.
+ * The heading command. It is used by the {@link heading.Heading heading feature} to apply headings.
*
- * @memberOf ui.icon
- * @extends ui.Controller
+ * @memberOf heading
+ * @extends core.command.Command
*/
-var Icon = function (_Controller) {
- inherits(Icon, _Controller);
+var HeadingCommand = function (_Command) {
+ inherits(HeadingCommand, _Command);
- /**
- * Creates an instance of {@link ui.icon.Icon} class.
+ /**
+ * Creates an instance of the command.
+ *
+ * @param {core.editor.Editor} editor Editor instance.
+ * @param {Array.} formats Heading formats to be used by the command instance.
+ */
+ function HeadingCommand(editor, formats) {
+ classCallCheck(this, HeadingCommand);
+
+ /**
+ * Heading formats used by this command.
*
- * @param {ui.icon.IconModel} model Model of this icon.
- * @param {ui.View} view View of this icon.
+ * @readonly
+ * @member {heading.HeadingFormat} heading.HeadingCommand#formats
*/
- function Icon(model, view) {
- classCallCheck(this, Icon);
-
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(Icon).call(this, model, view));
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(HeadingCommand).call(this, editor));
- view.bind('name').to(model);
- return _this;
- }
+ _this.formats = formats;
- return Icon;
-}(Controller);
+ /**
+ * The currently selected heading format.
+ *
+ * @readonly
+ * @observable
+ * @member {heading.HeadingFormat} heading.HeadingCommand#value
+ */
+ _this.set('value', _this.defaultFormat);
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ // Listen on selection change and set current command's format to format in the current selection.
+ _this.listenTo(editor.document.selection, 'change', function () {
+ var position = editor.document.selection.getFirstPosition();
+ var block = findTopmostBlock(position);
-/**
- * The icon view class.
- *
- * See {@link ui.icon.Icon}.
- *
- * @memberOf ui.icon
- * @extends ui.View
- */
+ if (block) {
+ var format = _this._getFormatById(block.name);
-var IconView = function (_View) {
- inherits(IconView, _View);
+ // TODO: What should happen if format is not found?
+ _this.value = format;
+ }
+ });
+ return _this;
+ }
/**
- * @inheritDoc
+ * The default format.
+ *
+ * @type {heading.HeadingFormat}
*/
- function IconView() {
- classCallCheck(this, IconView);
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(IconView).call(this));
- var bind = _this.bindTemplate;
+ createClass(HeadingCommand, [{
+ key: '_doExecute',
- _this.template = new Template({
- tag: 'svg',
- ns: 'http://www.w3.org/2000/svg',
- attributes: {
- class: ['ck-icon']
- },
- children: [{
- tag: 'use',
- ns: 'http://www.w3.org/2000/svg',
- attributes: {
- href: {
- ns: 'http://www.w3.org/1999/xlink',
- value: bind.to('name', function (i) {
- return '#ck-icon-' + i;
- })
- }
- }
- }]
- });
/**
- * The name of the icon. It corresponds with the name of the
- * file in the {@link ui.iconManager.IconManager}.
+ * Executes command.
*
- * @observable
- * @member {String} ui.icon.IconView#name
+ * @protected
+ * @param {Object} [options] Options for executed command.
+ * @param {String} [options.formatId] The identifier of the heading format that should be applied. It should be one of the
+ * {@link heading.HeadingFormat heading formats} provided to the command constructor. If this parameter is not provided,
+ * the value from {@link heading.HeadingCommand#defaultFormat defaultFormat} will be used.
+ * @param {engine.model.Batch} [options.batch] Batch to collect all the change steps.
+ * New batch will be created if this option is not set.
*/
- return _this;
- }
+ value: function _doExecute() {
+ var _this2 = this;
- return IconView;
-}(View);
+ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ // TODO: What should happen if format is not found?
+ var formatId = options.formatId || this.defaultFormat.id;
+ var doc = this.editor.document;
+ var selection = doc.selection;
+ var startPosition = selection.getFirstPosition();
+ var elements = [];
+ // Storing selection ranges and direction to fix selection after renaming. See ckeditor5-engine#367.
+ var ranges = [].concat(toConsumableArray(selection.getRanges()));
+ var isSelectionBackward = selection.isBackward;
+ // If current format is same as new format - toggle already applied format back to default one.
+ var shouldRemove = formatId === this.value.id;
-/**
- * The button controller class. It uses {@link ui.icon.Icon} component
- * to display an icon.
- *
- * const model = new Model( {
- * label: 'Bold',
- * isEnabled: true,
- * isOn: false,
- * icon: 'bold'
- * } );
- *
- * // An instance of Button with a label and an icon.
- * new Button( model, new ButtonView() );
- *
- * See {@link ui.button.ButtonView}.
- *
- * @memberOf ui.button
- * @extends ui.Controller
- */
+ // Collect elements to change format.
+ // This implementation may not be future proof but it's satisfactory at this stage.
+ if (selection.isCollapsed) {
+ var block = findTopmostBlock(startPosition);
-var Button = function (_Controller) {
- inherits(Button, _Controller);
+ if (block) {
+ elements.push(block);
+ }
+ } else {
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- /**
- * Creates an instance of {@link ui.button.Button} class.
- *
- * @param {ui.button.ButtonModel} model Model of this Button.
- * @param {ui.View} view View of this Button.
- */
- function Button(model, view) {
- classCallCheck(this, Button);
+ try {
+ for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var range = _step.value;
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(Button).call(this, model, view));
+ var startBlock = findTopmostBlock(range.start);
+ var endBlock = findTopmostBlock(range.end, false);
- view.bind('label', 'isOn', 'isEnabled', 'withText', 'type').to(model);
- view.bind('title').to(model, 'label', model, 'keystroke', function (label, keystroke) {
- if (keystroke) {
- label += ' (' + getEnvKeystrokeText(keystroke) + ')';
- }
+ elements.push(startBlock);
- return label;
- });
+ while (startBlock !== endBlock) {
+ startBlock = startBlock.nextSibling;
+ elements.push(startBlock);
+ }
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ }
- if (model.icon) {
- view.bind('icon').to(model);
- }
+ doc.enqueueChanges(function () {
+ var batch = options.batch || doc.batch();
- view.on('click', function () {
- return model.fire('execute');
- });
- return _this;
- }
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
- /**
- * @inheritDoc
+ try {
+ for (var _iterator2 = elements[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var element = _step2.value;
+
+ // When removing applied format.
+ if (shouldRemove) {
+ if (element.name === formatId) {
+ batch.rename(element, _this2.defaultFormat.id);
+ }
+ }
+ // When applying new format.
+ else {
+ batch.rename(element, formatId);
+ }
+ }
+
+ // If range's selection start/end is placed directly in renamed block - we need to restore it's position
+ // after renaming, because renaming puts new element there.
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+
+ doc.selection.setRanges(ranges, isSelectionBackward);
+ });
+ }
+
+ /**
+ * Returns the format by a given ID.
+ *
+ * @private
+ * @param {String} id
+ * @returns {heading.HeadingFormat}
*/
+ }, {
+ key: '_getFormatById',
+ value: function _getFormatById(id) {
+ return this.formats.find(function (item) {
+ return item.id === id;
+ }) || this.defaultFormat;
+ }
+ }, {
+ key: 'defaultFormat',
+ get: function get() {
+ // See https://github.com/ckeditor/ckeditor5/issues/98.
+ return this._getFormatById('paragraph');
+ }
+ }]);
+ return HeadingCommand;
+}(Command);
+
+function findTopmostBlock(position) {
+ var nodeAfter = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
- createClass(Button, [{
- key: 'init',
- value: function init() {
- if (this.model.icon) {
- this.addCollection('children');
+ var parent = position.parent;
+
+ // If position is placed inside root - get element after/before it.
+ if (parent instanceof RootElement) {
+ return nodeAfter ? position.nodeAfter : position.nodeBefore;
+ }
- var iconModel = new Model();
- iconModel.bind('name').to(this.model, 'icon');
+ while (!(parent.parent instanceof RootElement)) {
+ parent = parent.parent;
+ }
- this.add('children', new Icon(iconModel, new IconView()));
- }
+ return parent;
+}
- return get(Object.getPrototypeOf(Button.prototype), 'init', this).call(this);
- }
- }]);
- return Button;
-}(Controller);
+/**
+ * Heading format descriptor.
+ *
+ * @typedef {Object} heading.HeadingFormat
+ * @property {String} id Format identifier. It will be used as the element's name in the model.
+ * @property {String} viewElement The name of the view element that will be used to represent the model element in the view.
+ * @property {String} label The display name of the format.
+ */
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
+var formats = [{ id: 'paragraph', viewElement: 'p', label: 'Paragraph' }, { id: 'heading1', viewElement: 'h2', label: 'Heading 1' }, { id: 'heading2', viewElement: 'h3', label: 'Heading 2' }, { id: 'heading3', viewElement: 'h4', label: 'Heading 3' }];
+
/**
- * The button view class.
- *
- * See {@link ui.button.Button}.
+ * The headings engine feature. It handles switching between block formats – headings and paragraph.
+ * This class represents the engine part of the heading feature. See also {@link heading.Heading}.
*
- * @memberOf ui.button
- * @extends ui.View
+ * @memberOf heading
+ * @extends core.Feature
*/
-var ButtonView = function (_View) {
- inherits(ButtonView, _View);
+var HeadingEngine = function (_Feature) {
+ inherits(HeadingEngine, _Feature);
- /**
- * @inheritDoc
- */
- function ButtonView() {
- classCallCheck(this, ButtonView);
+ function HeadingEngine() {
+ classCallCheck(this, HeadingEngine);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(HeadingEngine).apply(this, arguments));
+ }
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ButtonView).call(this));
+ createClass(HeadingEngine, [{
+ key: 'init',
- var bind = _this.bindTemplate;
- _this.template = new Template({
- tag: 'button',
+ /**
+ * @inheritDoc
+ */
+ value: function init() {
+ var editor = this.editor;
+ var data = editor.data;
+ var editing = editor.editing;
- attributes: {
- class: ['ck-button', bind.to('isEnabled', function (value) {
- return value ? 'ck-enabled' : 'ck-disabled';
- }), bind.to('isOn', function (value) {
- return value ? 'ck-on' : 'ck-off';
- }), bind.if('withText', 'ck-button_with-text')],
- title: [bind.to('title')],
- type: bind.to('type', function (value) {
- return value ? value : 'button';
- })
- },
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- children: [{
- tag: 'span',
+ try {
+ for (var _iterator = formats[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var format = _step.value;
- attributes: {
- class: ['ck-button__label']
- },
+ // Skip paragraph - it is defined in required Paragraph feature.
+ if (format.id !== 'paragraph') {
+ // Schema.
+ editor.document.schema.registerItem(format.id, '$block');
- children: [{
- text: bind.to('label')
- }]
- }],
+ // Build converter from model to view for data and editing pipelines.
+ buildModelConverter().for(data.modelToView, editing.modelToView).fromElement(format.id).toElement(format.viewElement);
- on: {
- mousedown: bind.to(function (evt) {
- evt.preventDefault();
- }),
+ // Build converter from view to model for data pipeline.
+ buildViewConverter().for(data.viewToModel).fromElement(format.viewElement).toElement(format.id);
+ }
+ }
- click: bind.to(function (evt) {
- // We can't make the button disabled using the disabled attribute, because it won't be focusable.
- // Though, shouldn't this condition be moved to the button controller?
- if (_this.isEnabled) {
- _this.fire('click');
- } else {
- evt.preventDefault();
+ // Register the heading command.
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
}
- })
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
}
- });
-
- _this.register('children', function (el) {
- return el;
- });
-
- /**
- * The label of the button view visible to the user.
- *
- * @observable
- * @member {String} ui.button.ButtonView#label
- */
- /**
- * The HTML type of the button. Default `button`.
- *
- * @observable
- * @member {'button'|'submit'|'reset'|'menu'} ui.button.ButtonView#type
- */
+ var command = new HeadingCommand(editor, formats);
+ editor.commands.set('heading', command);
- /**
- * Controls whether the button view is "on", e.g. some feature which it represents
- * is currently enabled.
- *
- * @observable
- * @member {Boolean} ui.button.ButtonView#isOn
- */
+ // If the enter command is added to the editor, alter its behavior.
+ // Enter at the end of a heading element should create a paragraph.
+ var enterCommand = editor.commands.get('enter');
- /**
- * Controls whether the button view is enabled (can be clicked).
- *
- * @observable
- * @member {Boolean} ui.button.ButtonView#isEnabled
- */
+ if (enterCommand) {
+ this.listenTo(enterCommand, 'afterExecute', function (evt, data) {
+ var positionParent = editor.document.selection.getFirstPosition().parent;
+ var batch = data.batch;
+ var isHeading = formats.some(function (format) {
+ return format.id == positionParent.name;
+ });
- /**
- * (Optional) Whether the label of the button is hidden (e.g. button with icon only).
- *
- * @observable
- * @member {Boolean} ui.button.ButtonView#withText
- */
+ if (isHeading && positionParent.name != command.defaultFormat.id && positionParent.childCount === 0) {
+ batch.rename(positionParent, command.defaultFormat.id);
+ }
+ });
+ }
+ }
+ }], [{
+ key: 'requires',
/**
- * (Optional) Title of the button displayed in the tooltip, i.e. when
- * hovering the button with the mouse cursor.
- *
- * @observable
- * @member {Boolean} ui.button.ButtonView#title
+ * @inheritDoc
*/
- return _this;
- }
-
- return ButtonView;
-}(View);
+ get: function get() {
+ return [Paragraph];
+ }
+ }]);
+ return HeadingEngine;
+}(Feature);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -46658,70 +45518,79 @@ var ButtonView = function (_View) {
*/
/**
- * The bold feature. It introduces the Bold button and the Ctrl+B keystroke.
+ * The dropdown button view class.
*
- * It uses the {@link basic-styles.BoldEngine bold engine feature}.
+ * See {@link ui.dropdown.Dropdown}, {@link ui.button.Button}, {@link ui.button.ButtonView}.
*
- * @memberOf basic-styles
- * @extends core.Feature
+ * @memberOf ui.dropdown
+ * @extends ui.button.ButtonView
*/
-var Bold = function (_Feature) {
- inherits(Bold, _Feature);
+var DropdownButtonView = function (_ButtonView) {
+ inherits(DropdownButtonView, _ButtonView);
- function Bold() {
- classCallCheck(this, Bold);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Bold).apply(this, arguments));
+ /**
+ * @inheritDoc
+ */
+ function DropdownButtonView() {
+ classCallCheck(this, DropdownButtonView);
+
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DropdownButtonView).call(this));
+
+ Template.extend(_this.template, {
+ attributes: {
+ class: 'ck-dropdown__button'
+ }
+ });
+ return _this;
}
- createClass(Bold, [{
- key: 'init',
+ return DropdownButtonView;
+}(ButtonView);
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- /**
- * @inheritDoc
- */
- value: function init() {
- var editor = this.editor;
- var t = editor.t;
- var command = editor.commands.get('bold');
- var keystroke = 'CTRL+B';
+/**
+ * The dropdown panel controller class.
+ *
+ * const model = new Model( {
+ * isVisible: false,
+ * } );
+ *
+ * // An instance of DropdownPanelView.
+ * new DropdownPanel( model, new DropdownPanelView() );
+ *
+ * See {@link ui.dropdown.DropdownPanelView}.
+ *
+ * @memberOf ui.dropdown
+ * @extends ui.Controller
+ */
- // Create button model.
- var buttonModel = new Model({
- isEnabled: true,
- isOn: false,
- label: t('Bold'),
- icon: 'bold',
- keystroke: keystroke
- });
+var DropdownPanel = function (_Controller) {
+ inherits(DropdownPanel, _Controller);
- // Bind button model to command.
- buttonModel.bind('isOn', 'isEnabled').to(command, 'value', 'isEnabled');
+ /**
+ * Creates an instance of {@link ui.dropdown.DropdownPanel} class.
+ *
+ * @param {ui.dropdown.DropdownPanel} model Model of this dropdown panel.
+ * @param {ui.View} view View of this dropdown panel.
+ */
+ function DropdownPanel(model, view) {
+ classCallCheck(this, DropdownPanel);
- // Execute command.
- this.listenTo(buttonModel, 'execute', function () {
- return editor.execute('bold');
- });
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DropdownPanel).call(this, model, view));
- // Add bold button to feature components.
- editor.ui.featureComponents.add('bold', Button, ButtonView, buttonModel);
+ view.bind('isVisible').to(model);
- // Set the Ctrl+B keystroke.
- editor.keystrokes.set(keystroke, 'bold');
- }
- }], [{
- key: 'requires',
+ _this.addCollection('content');
+ return _this;
+ }
- /**
- * @inheritDoc
- */
- get: function get() {
- return [BoldEngine];
- }
- }]);
- return Bold;
-}(Feature);
+ return DropdownPanel;
+}(Controller);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -46729,70 +45598,50 @@ var Bold = function (_Feature) {
*/
/**
- * The italic feature. It introduces the Italic button and the Ctrl+I keystroke.
+ * The dropdown panel view class.
*
- * It uses the {@link basic-styles.ItalicEngine italic engine feature}.
+ * See {@link ui.dropdown.DropdownPanel}.
*
- * @memberOf basic-styles
- * @extends core.Feature
+ * @memberOf ui.dropdown
+ * @extends ui.View
*/
-var Italic = function (_Feature) {
- inherits(Italic, _Feature);
-
- function Italic() {
- classCallCheck(this, Italic);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Italic).apply(this, arguments));
- }
-
- createClass(Italic, [{
- key: 'init',
-
+var DropdownPanelView = function (_View) {
+ inherits(DropdownPanelView, _View);
- /**
- * @inheritDoc
- */
- value: function init() {
- var editor = this.editor;
- var t = editor.t;
- var command = editor.commands.get('italic');
- var keystroke = 'CTRL+I';
+ /**
+ * @inheritDoc
+ */
+ function DropdownPanelView() {
+ classCallCheck(this, DropdownPanelView);
- // Create button model.
- var buttonModel = new Model({
- isEnabled: true,
- isOn: false,
- label: t('Italic'),
- icon: 'italic',
- keystroke: keystroke
- });
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DropdownPanelView).call(this));
- // Bind button model to command.
- buttonModel.bind('isOn', 'isEnabled').to(command, 'value', 'isEnabled');
+ var bind = _this.bindTemplate;
- // Execute command.
- this.listenTo(buttonModel, 'execute', function () {
- return editor.execute('italic');
- });
+ _this.template = new Template({
+ tag: 'div',
- // Add bold button to feature components.
- editor.ui.featureComponents.add('italic', Button, ButtonView, buttonModel);
+ attributes: {
+ class: ['ck-reset', 'ck-dropdown__panel', bind.if('isVisible', 'ck-dropdown__panel-visible')]
+ }
+ });
- // Set the Ctrl+I keystroke.
- editor.keystrokes.set(keystroke, 'italic');
- }
- }], [{
- key: 'requires',
+ _this.register('content', function (el) {
+ return el;
+ });
/**
- * @inheritDoc
+ * Controls whether the panel is visible.
+ *
+ * @observable
+ * @member {Boolean} ui.dropdown.DropdownPanelView#isVisible
*/
- get: function get() {
- return [ItalicEngine];
- }
- }]);
- return Italic;
-}(Feature);
+ return _this;
+ }
+
+ return DropdownPanelView;
+}(View);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -46800,79 +45649,99 @@ var Italic = function (_Feature) {
*/
/**
- * Facade over the native [`DataTransfer`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer) object.
+ * The dropdown controller class.
*
- * @memberOf clipboard
+ * const model = new Model( {
+ * label: 'Dropdown',
+ * isEnabled: true,
+ * inOn: false
+ * } );
+ *
+ * // An instance of Dropdown.
+ * new Dropdown( model, new DropdownView() );
+ *
+ * See {@link ui.dropdown.DropdownView}.
+ *
+ * @memberOf ui.dropdown
+ * @extends ui.Controller
*/
-var DataTransfer = function () {
- function DataTransfer(nativeDataTransfer) {
- classCallCheck(this, DataTransfer);
- /**
- * The native DataTransfer object.
- *
- * @private
- * @member {DataTransfer} clipboard.DataTransfer#_native
- */
- this._native = nativeDataTransfer;
- }
+var Dropdown = function (_Controller) {
+ inherits(Dropdown, _Controller);
/**
- * Gets data from the data transfer by its mime type.
+ * Creates an instance of {@link ui.dropdown.Dropdown} class.
*
- * dataTransfer.getData( 'text/plain' );
+ * @param {ui.dropdown.DropdownModel} model Model of this dropdown.
+ * @param {ui.View} view View of this dropdown.
+ */
+ function Dropdown(model, view) {
+ classCallCheck(this, Dropdown);
+
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(Dropdown).call(this, model, view));
+
+ _this.addCollection('main');
+ _this._createButton();
+ _this._createPanel();
+ return _this;
+ }
+
+ /**
+ * Creates {@link ui.dropdown.Dropdown#button} of this dropdown.
*
- * @param {String} type The mime type. E.g. `text/html` or `text/plain`.
- * @returns {String}
+ * @protected
*/
- createClass(DataTransfer, [{
- key: "getData",
- value: function getData(type) {
- return this._native.getData(type);
- }
- }]);
- return DataTransfer;
-}();
+ createClass(Dropdown, [{
+ key: '_createButton',
+ value: function _createButton() {
+ var model = this.model;
+ var view = this.view;
+ var buttonModel = new Model();
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ // Button needs a separate Model because otherwise it would fire #execute event
+ // on the model shared between multiple dropdowns (they would all open at the same time).
+ buttonModel.bind('label', 'isOn', 'isEnabled', 'withText').to(model);
-/**
- * {@link engine.view.Document#paste Paste} event observer.
- *
- * Note that this observer is not available by default. To make it available it needs to be added to {@link engine.view.Document}
- * by the {@link engine.view.Document#addObserver} method.
- *
- * @memberOf clipboard
- * @extends engine.view.observer.DomEventObserver
- */
+ /**
+ * Button of this dropdown.
+ *
+ * @readonly
+ * @member {ui.button.Button} ui.dropdown.Dropdown#button
+ */
+ this.add('main', this.button = new Button(buttonModel, new DropdownButtonView()));
-var ClipboardObserver = function (_DomEventObserver) {
- inherits(ClipboardObserver, _DomEventObserver);
+ // When ui.dropdown.Dropdown#button is clicked switch the open/closed state of the Dropdown.
+ this.listenTo(buttonModel, 'execute', function () {
+ return view.isOpen = !view.isOpen;
+ });
+ }
- function ClipboardObserver(doc) {
- classCallCheck(this, ClipboardObserver);
+ /**
+ * Creates {@link ui.dropdown.Dropdown#panel} of this dropdown.
+ *
+ * @protected
+ */
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ClipboardObserver).call(this, doc));
+ }, {
+ key: '_createPanel',
+ value: function _createPanel() {
+ var panelModel = new Model();
- _this.domEventType = 'paste';
- return _this;
- }
+ panelModel.bind('isVisible').to(this.view, 'isOpen');
- createClass(ClipboardObserver, [{
- key: 'onDomEvent',
- value: function onDomEvent(domEvent) {
- this.fire(domEvent.type, domEvent, {
- dataTransfer: new DataTransfer(domEvent.clipboardData)
- });
- }
- }]);
- return ClipboardObserver;
-}(DomEventObserver);
+ /**
+ * Panel of this dropdown.
+ *
+ * @readonly
+ * @member {ui.dropdown.DropdownPanel} ui.dropdown.Dropdown#panel
+ */
+ this.add('main', this.panel = new DropdownPanel(panelModel, new DropdownPanelView()));
+ }
+ }]);
+ return Dropdown;
+}(Controller);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -46880,34 +45749,41 @@ var ClipboardObserver = function (_DomEventObserver) {
*/
/**
- * Converts plain text to its HTML-ized version.
+ * The list item controller class.
*
- * @function clipboard.utils.plainTextToHtml
- * @param {String} text The plain text to convert.
- * @returns {String} HTML generated from the plain text.
+ * @memberOf ui.list
+ * @extends ui.Controller
*/
-function plainTextToHtml(text) {
- text = text
- // Encode <>.
- .replace(//g, '>')
- // Creates paragraphs for double line breaks and change single line breaks to spaces.
- // In the future single line breaks may be converted into s.
- .replace(/\n\n/g, '
').replace(/\n/g, ' ')
- // Preserve trailing spaces (only the first and last one – the rest is handled below).
- .replace(/^\s/, ' ').replace(/\s$/, ' ')
- // Preserve other subsequent spaces now.
- .replace(/\s\s/g, ' ');
- if (text.indexOf('
') > -1) {
- // If we created paragraphs above, add the trailing ones.
- text = '
' + text + '
';
- }
+var ListItem = function (_Controller) {
+ inherits(ListItem, _Controller);
- // TODO:
- // * What about '\nfoo' vs ' foo'?
+ /**
+ * Creates an instance of {@link ui.list.ListItem} class.
+ *
+ * @param {ui.list.ListItemModel} model Model of this list item.
+ * @param {ui.View} view View of this list item.
+ */
+ function ListItem(model, view) {
+ classCallCheck(this, ListItem);
- return text;
-}
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListItem).call(this, model, view));
+
+ view.bind('label').to(model);
+
+ if (model.style) {
+ view.bind('style').to(model);
+ }
+
+ // TODO: Event delegation with altered event name.
+ view.on('click', function () {
+ return model.fire('execute');
+ });
+ return _this;
+ }
+
+ return ListItem;
+}(Controller);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -46915,23 +45791,68 @@ function plainTextToHtml(text) {
*/
/**
- * Removes some popular browser quirks out of the clipboard data (HTML).
+ * The list item view class.
*
- * @function clipboard.utils.normalizeClipboardData
- * @param {String} data The HTML data to normalize.
- * @returns {String} Normalized HTML.
+ * See {@link ui.list.ListItem}.
+ *
+ * @memberOf ui.list
+ * @extends ui.View
*/
-function normalizeClipboardData(data) {
- return data.replace(/(\s+)<\/span>/g, function (fullMatch, spaces) {
- // Handle the most popular and problematic case when even a single space becomes an nbsp;.
- // Decode those to normal spaces. Read more in https://github.com/ckeditor/ckeditor5-clipboard/issues/2.
- if (spaces.length == 1) {
- return ' ';
- }
- return spaces;
- });
-}
+var ListItemView = function (_View) {
+ inherits(ListItemView, _View);
+
+ /**
+ * @inheritDoc
+ */
+ function ListItemView() {
+ classCallCheck(this, ListItemView);
+
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListItemView).call(this));
+
+ var bind = _this.bindTemplate;
+
+ _this.template = new Template({
+ tag: 'li',
+
+ attributes: {
+ class: ['ck-list__item'],
+ style: bind.to('style')
+ },
+
+ children: [{
+ text: bind.to('label')
+ }],
+
+ on: {
+ click: bind.to('click')
+ }
+ });
+
+ /**
+ * The label of the list item.
+ *
+ * @observable
+ * @member {String} ui.list.ListItemView#label
+ */
+
+ /**
+ * (Optional) The DOM style attribute of the list item.
+ *
+ * @observable
+ * @member {String} ui.list.ListItemView#style
+ */
+
+ /**
+ * Fired when the list item has been clicked.
+ *
+ * @event ui.list.ListItemView#click
+ */
+ return _this;
+ }
+
+ return ListItemView;
+}(View);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -46939,121 +45860,92 @@ function normalizeClipboardData(data) {
*/
/**
- * The clipboard feature. Currently, it's only responsible for intercepting the `paste` event and
- * passing the pasted content through the clipboard pipeline.
- *
- * ## Clipboard Pipeline
- *
- * The feature creates the clipboard pipeline which allows for processing clipboard content
- * before it gets inserted into the editor. The pipeline consists of two events on which
- * the features can listen in order to modify or totally override the default behavior.
- *
- * ### On {@link engine.view.Document#paste}
- *
- * The default action is to:
- *
- * 1. get HTML or plain text from the clipboard,
- * 2. fire {@link engine.view.Document#clipboardInput} with the clipboard data parsed to
- * a {@link engine.view.DocumentFragment view document fragment},
- * 3. prevent the default action of the native `paste` event.
- *
- * This action is performed by a low priority listener, so it can be overridden by a normal one.
- * You'd only need to do this when a deeper change in pasting behavior was needed. For example,
- * a feature which wants to differently read data from the clipboard (the {@link clipboard.DataTransfer `DataTransfer`}).
- * should plug a listener at this stage.
- *
- * ### On {@link engine.view.Document#clipboardInput}
+ * The list controller class.
*
- * The default action is to insert the content (`data.content`, represented by a {@link engine.view.DocumentFragment})
- * to an editor if the data is not empty.
+ * const itemsCollection = new Collection();
*
- * This action is performed by a low priority listener, so it can be overridden by a normal one.
+ * itemsCollection.add( new Model( { label: 'foo' } ) );
+ * itemsCollection.add( new Model( { label: 'bar' } ) );
*
- * At this stage the pasted content can be processed by the features. E.g. a feature which wants to transform
- * a pasted text into a link can be implemented in this way:
+ * const model = new Model( {
+ * items: itemsCollection
+ * } );
*
- * this.listenTo( editor.editing.view, 'clipboardInput', ( evt, data ) => {
- * if ( data.content.childCount == 1 && isUrlText( data.content.getChild( 0 ) ) ) {
- * const linkUrl = data.content.getChild( 0 ).data;
+ * // An instance of List filled up with the `itemsCollection`.
+ * // Any change to `itemsCollection` will be reflected in DOM.
+ * new List( model, new ListView() );
*
- * data.content = new ViewDocumentFragment( [
- * ViewElement(
- * 'a',
- * { href: linkUrl },
- * [ new ViewText( linkUrl ) ]
- * )
- * ] );
- * }
- * } );
+ * See {@link ui.list.ListView}, {@link ui.list.ListItem}.
*
- * @memberOf clipboard
- * @extends core.Feature
+ * @memberOf ui.list
+ * @extends ui.Controller
*/
-var Clipboard = function (_Feature) {
- inherits(Clipboard, _Feature);
+var List = function (_Controller) {
+ inherits(List, _Controller);
- function Clipboard() {
- classCallCheck(this, Clipboard);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Clipboard).apply(this, arguments));
- }
+ /**
+ * Creates an instance of {@link ui.list.List} class.
+ *
+ * @param {ui.list.ListModel} model Model of this list.
+ * @param {ui.View} view View of this list.
+ */
+ function List(model, view) {
+ classCallCheck(this, List);
- createClass(Clipboard, [{
- key: 'init',
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(List).call(this, model, view));
- /**
- * @inheritDoc
- */
- value: function init() {
- var _this2 = this;
+ var list = _this.addCollection('list');
- var editor = this.editor;
- var editingView = editor.editing.view;
+ list.bind(model.items).as(ListItem, ListItemView);
+ list.delegate('execute').to(model);
+ return _this;
+ }
- /**
- * Data processor used to convert pasted HTML to a view structure.
- *
- * @private
- * @member {engine.dataProcessor.HtmlDataProcessor} clipboard.Clipboard#_htmlDataProcessor
- */
- this._htmlDataProcessor = new HtmlDataProcessor();
+ return List;
+}(Controller);
- editingView.addObserver(ClipboardObserver);
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- // The clipboard pipeline.
+/**
+ * The list view class.
+ *
+ * See {@link ui.list.List}.
+ *
+ * @memberOf ui.list
+ * @extends ui.View
+ */
- this.listenTo(editingView, 'paste', function (evt, data) {
- var dataTransfer = data.dataTransfer;
- var content = '';
+var ListView = function (_View) {
+ inherits(ListView, _View);
- if (dataTransfer.getData('text/html')) {
- content = normalizeClipboardData(dataTransfer.getData('text/html'));
- } else if (dataTransfer.getData('text/plain')) {
- content = plainTextToHtml(dataTransfer.getData('text/plain'));
- }
+ /**
+ * @inheritDoc
+ */
+ function ListView() {
+ classCallCheck(this, ListView);
- content = _this2._htmlDataProcessor.toView(content);
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListView).call(this));
- data.preventDefault();
+ _this.template = new Template({
+ tag: 'ul',
- editingView.fire('clipboardInput', { dataTransfer: dataTransfer, content: content });
- }, { priority: 'low' });
+ attributes: {
+ class: ['ck-reset', 'ck-list']
+ }
+ });
- this.listenTo(editingView, 'clipboardInput', function (evt, data) {
- if (!data.content.isEmpty) {
- (function () {
- var doc = editor.document;
+ _this.register('list', function (el) {
+ return el;
+ });
+ return _this;
+ }
- doc.enqueueChanges(function () {
- _this2.editor.data.insert(data.content, doc.selection);
- });
- })();
- }
- }, { priority: 'low' });
- }
- }]);
- return Clipboard;
-}(Feature);
+ return ListView;
+}(View);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -47061,127 +45953,162 @@ var Clipboard = function (_Feature) {
*/
/**
- * Enter command. It is used by the {@link enter.Enter Enter feature} to handle the Enter key.
+ * The list dropdown controller class. It represents a dropdown
+ * with a {@link ui.list.List} component.
*
- * @member enter
- * @extends core.command.Command
+ * const model = new Model( {
+ * label: 'List Dropdown',
+ * isEnabled: true,
+ * isOn: false,
+ * content: {@link ui.dropdown.list.ListDropdownModel#content}
+ * } );
+ *
+ * // An instance of Dropdown.
+ * new ListDropdown( model, new ListDropdownView() );
+ *
+ * See {@link ui.dropdown.list.ListDropdownView}.
+ *
+ * @memberOf ui.dropdown.list
+ * @extends ui.dropdown.Dropdown
*/
-var EnterCommand = function (_Command) {
- inherits(EnterCommand, _Command);
+var ListDropdown = function (_Dropdown) {
+ inherits(ListDropdown, _Dropdown);
- function EnterCommand() {
- classCallCheck(this, EnterCommand);
- return possibleConstructorReturn(this, Object.getPrototypeOf(EnterCommand).apply(this, arguments));
- }
+ /**
+ * Creates an instance of {@link ui.dropdown.list.ListDropdown} class.
+ *
+ * @param {ui.dropdown.list.ListDropdownModel} model Model of this list dropdown.
+ * @param {ui.View} view View of this list dropdown.
+ */
+ function ListDropdown(model, view) {
+ classCallCheck(this, ListDropdown);
- createClass(EnterCommand, [{
- key: '_doExecute',
+ /**
+ * List of this list dropdown.
+ *
+ * @readonly
+ * @member {ui.list.List} ui.dropdown.list.ListDropdown#list
+ */
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListDropdown).call(this, model, view));
- /**
- * @inheritDoc
- */
- value: function _doExecute() {
- var _this2 = this;
+ _this.list = new List(_this.model.content, new ListView());
- var doc = this.editor.document;
- var batch = doc.batch();
+ // Delegate ui.list.ListModel#execute to the model.
+ _this.model.content.delegate('execute').to(model);
- doc.enqueueChanges(function () {
- enterBlock(batch, doc.selection);
+ // Collapse the dropdown when an item in the panel is clicked.
+ _this.listenTo(model, 'execute', function () {
+ view.isOpen = false;
+ });
- _this2.fire('afterExecute', { batch: batch });
- });
- }
- }]);
- return EnterCommand;
-}(Command);
+ _this.panel.add('content', _this.list);
+ return _this;
+ }
-function enterBlock(batch, selection) {
- var doc = batch.document;
- var isSelectionEmpty = selection.isCollapsed;
- var range = selection.getFirstRange();
- var startElement = range.start.parent;
- var endElement = range.end.parent;
+ return ListDropdown;
+}(Dropdown);
- // Don't touch the root.
- if (startElement.root == startElement) {
- if (!isSelectionEmpty) {
- doc.composer.deleteContents(batch, selection);
- }
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- return;
- }
+/**
+ * The dropdown view class.
+ *
+ * See {@link ui.dropdown.Dropdown}.
+ *
+ * @memberOf ui.dropdown
+ * @extends ui.View
+ */
- if (isSelectionEmpty) {
- splitBlock(batch, selection, range.start);
- } else {
- var shouldMerge = range.start.isAtStart && range.end.isAtEnd;
- var isContainedWithinOneElement = startElement == endElement;
+var DropdownView = function (_View) {
+ inherits(DropdownView, _View);
- doc.composer.deleteContents(batch, selection, { merge: shouldMerge });
+ /**
+ * @inheritDoc
+ */
+ function DropdownView() {
+ classCallCheck(this, DropdownView);
- if (!shouldMerge) {
- // Partially selected elements.
- //
- // x[xx]x -> x^x -> x ^x
- if (isContainedWithinOneElement) {
- splitBlock(batch, selection, selection.focus);
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DropdownView).call(this));
+
+ _this.set('isOpen', false);
+
+ _this.template = new Template({
+ tag: 'div',
+
+ attributes: {
+ class: ['ck-dropdown']
}
- // Selection over multiple elements.
- //
- // x[x y]y
-> x^
y
-> x ^y
- else {
- selection.collapse(endElement);
- }
- }
+ });
+
+ _this.register('main', function (el) {
+ return el;
+ });
+
+ /**
+ * Controls whether the dropdown view is open, which also means its
+ * {@link ui.dropdown.Dropdown#panel} is visible.
+ *
+ * @observable
+ * @member {Boolean} ui.dropdown.DropdownViewl#isOpen
+ */
+ return _this;
}
-}
-function splitBlock(batch, selection, splitPos) {
- batch.split(splitPos);
- selection.collapse(splitPos.parent.nextSibling);
-}
+ return DropdownView;
+}(View);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
+/* globals document */
+
/**
- * Enter observer introduces the {@link engine.view.Document#enter} event.
+ * The list dropdown view class.
*
- * @memberOf enter
- * @extends engine.view.observer.Observer
+ * See {@link ui.dropdown.list.ListDropdown}.
+ *
+ * @memberOf ui.dropdown.list
+ * @extends ui.dropdown.DropdownView
*/
-var EnterObserver = function (_Observer) {
- inherits(EnterObserver, _Observer);
-
- function EnterObserver(document) {
- classCallCheck(this, EnterObserver);
+var ListDropdownView = function (_DropdownView) {
+ inherits(ListDropdownView, _DropdownView);
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(EnterObserver).call(this, document));
+ /**
+ * @inheritDoc
+ */
+ function ListDropdownView() {
+ classCallCheck(this, ListDropdownView);
- document.on('keydown', function (evt, data) {
- if (_this.isEnabled && data.keyCode == keyCodes.enter) {
- document.fire('enter', new DomEventData(document, data.domEvent));
- }
- });
- return _this;
- }
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListDropdownView).call(this));
- /**
- * @inheritDoc
- */
+ _this.listenTo(_this, 'change:isOpen', function (evt, name, value) {
+ if (value) {
+ // TODO: It will probably be focus/blur-based rather than click. It should be bound
+ // to focusmanager of some sort.
+ _this.listenTo(document, 'click', function (evtInfo, _ref) {
+ var domEvtTarget = _ref.target;
+ // Collapse the dropdown when the webpage outside of the component is clicked.
+ if (_this.element != domEvtTarget && !_this.element.contains(domEvtTarget)) {
+ _this.isOpen = false;
+ }
+ });
+ } else {
+ _this.stopListening(document);
+ }
+ });
+ return _this;
+ }
- createClass(EnterObserver, [{
- key: 'observe',
- value: function observe() {}
- }]);
- return EnterObserver;
-}(Observer);
+ return ListDropdownView;
+}(DropdownView);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -47189,38 +46116,103 @@ var EnterObserver = function (_Observer) {
*/
/**
- * The Enter feature. Handles the Enter and Shift + Enter keys in the editor.
+ * The headings feature. It introduces the `headings` drop-down list and the `heading` command which allow
+ * to convert paragraphs into headings.
*
- * @memberOf enter
+ * @memberOf heading
* @extends core.Feature
*/
-var Enter = function (_Feature) {
- inherits(Enter, _Feature);
-
- function Enter() {
- classCallCheck(this, Enter);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Enter).apply(this, arguments));
+var Heading = function (_Feature) {
+ inherits(Heading, _Feature);
+
+ function Heading() {
+ classCallCheck(this, Heading);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Heading).apply(this, arguments));
}
- createClass(Enter, [{
+ createClass(Heading, [{
key: 'init',
+
+
+ /**
+ * @inheritDoc
+ */
value: function init() {
var editor = this.editor;
- var editingView = editor.editing.view;
+ var command = editor.commands.get('heading');
+ var formats = command.formats;
+ var collection = new Collection();
- editingView.addObserver(EnterObserver);
+ // Add formats to collection.
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- editor.commands.set('enter', new EnterCommand(editor));
+ try {
+ for (var _iterator = formats[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var format = _step.value;
- // TODO We may use the keystroke handler for that.
- this.listenTo(editingView, 'enter', function (evt, data) {
- editor.execute('enter');
- data.preventDefault();
- }, { priority: 'low' });
+ collection.add(new Model({
+ id: format.id,
+ label: format.label
+ }));
+ }
+
+ // Create dropdown model.
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+
+ var dropdownModel = new Model({
+ isEnabled: true,
+ isOn: false,
+ label: 'Heading',
+ withText: true,
+
+ // Create item list model.
+ content: new Model({
+ items: collection
+ })
+ });
+
+ // Bind dropdown model to command.
+ dropdownModel.bind('isEnabled').to(command, 'isEnabled');
+ dropdownModel.bind('label').to(command, 'value', function (format) {
+ return format.label;
+ });
+
+ // Execute command when an item from the dropdown is selected.
+ this.listenTo(dropdownModel, 'execute', function (evt) {
+ editor.execute('heading', { formatId: evt.source.id });
+ editor.editing.view.focus();
+ });
+
+ // Register UI component.
+ editor.ui.featureComponents.add('headings', ListDropdown, ListDropdownView, dropdownModel);
+ }
+ }], [{
+ key: 'requires',
+
+ /**
+ * @inheritDoc
+ */
+ get: function get() {
+ return [HeadingEngine];
}
}]);
- return Enter;
+ return Heading;
}(Feature);
/**
@@ -47229,35 +46221,35 @@ var Enter = function (_Feature) {
*/
/**
- * The dropdown button view class.
+ * {@link engine.view.Document#click Click} event observer.
*
- * See {@link ui.dropdown.Dropdown}, {@link ui.button.Button}, {@link ui.button.ButtonView}.
+ * Note that this observer is not available by default. To make it available it needs to be added to {@link engine.view.Document}
+ * by a {@link engine.view.Document#addObserver} method.
*
- * @memberOf ui.dropdown
- * @extends ui.button.ButtonView
+ * @memberOf engine.view.observer
+ * @extends engine.view.observer.DomEventObserver
*/
-var DropdownButtonView = function (_ButtonView) {
- inherits(DropdownButtonView, _ButtonView);
+var ClickObserver = function (_DomEventObserver) {
+ inherits(ClickObserver, _DomEventObserver);
- /**
- * @inheritDoc
- */
- function DropdownButtonView() {
- classCallCheck(this, DropdownButtonView);
+ function ClickObserver(document) {
+ classCallCheck(this, ClickObserver);
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DropdownButtonView).call(this));
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ClickObserver).call(this, document));
- Template.extend(_this.template, {
- attributes: {
- class: 'ck-dropdown__button'
- }
- });
- return _this;
- }
+ _this.domEventType = 'click';
+ return _this;
+ }
- return DropdownButtonView;
-}(ButtonView);
+ createClass(ClickObserver, [{
+ key: 'onDomEvent',
+ value: function onDomEvent(domEvent) {
+ this.fire(domEvent.type, domEvent);
+ }
+ }]);
+ return ClickObserver;
+}(DomEventObserver);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -47265,43 +46257,24 @@ var DropdownButtonView = function (_ButtonView) {
*/
/**
- * The dropdown panel controller class.
- *
- * const model = new Model( {
- * isVisible: false,
- * } );
- *
- * // An instance of DropdownPanelView.
- * new DropdownPanel( model, new DropdownPanelView() );
- *
- * See {@link ui.dropdown.DropdownPanelView}.
+ * This class is to mark specific {@link engine.view.Node} as {@link link.LinkElement}.
+ * E.g. There could be a situation when different features will create nodes with the same names,
+ * and hence they must be identified somehow.
*
- * @memberOf ui.dropdown
- * @extends ui.Controller
+ * @memberOf link
+ * @extends engine.view.AttributeElement
*/
-var DropdownPanel = function (_Controller) {
- inherits(DropdownPanel, _Controller);
-
- /**
- * Creates an instance of {@link ui.dropdown.DropdownPanel} class.
- *
- * @param {ui.dropdown.DropdownPanel} model Model of this dropdown panel.
- * @param {ui.View} view View of this dropdown panel.
- */
- function DropdownPanel(model, view) {
- classCallCheck(this, DropdownPanel);
-
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DropdownPanel).call(this, model, view));
-
- view.bind('isVisible').to(model);
+var LinkElement = function (_AttributeElement) {
+ inherits(LinkElement, _AttributeElement);
- _this.addCollection('content');
- return _this;
+ function LinkElement() {
+ classCallCheck(this, LinkElement);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(LinkElement).apply(this, arguments));
}
- return DropdownPanel;
-}(Controller);
+ return LinkElement;
+}(AttributeElement);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -47309,50 +46282,38 @@ var DropdownPanel = function (_Controller) {
*/
/**
- * The dropdown panel view class.
- *
- * See {@link ui.dropdown.DropdownPanel}.
+ * Walk backward and forward from start position, node by node as long as they have the same `linkHref` attribute value and return
+ * {@link engine.model.Range Range} with found link.
*
- * @memberOf ui.dropdown
- * @extends ui.View
+ * @param {engine.model.Position} position Start position.
+ * @param {String} value `linkHref` attribute value.
+ * @returns {engine.model.Range} Link range.
*/
+function findLinkRange(position, value) {
+ return new Range$1(_findBound(position, value, true), _findBound(position, value, false));
+}
-var DropdownPanelView = function (_View) {
- inherits(DropdownPanelView, _View);
-
- /**
- * @inheritDoc
- */
- function DropdownPanelView() {
- classCallCheck(this, DropdownPanelView);
-
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DropdownPanelView).call(this));
-
- var bind = _this.bindTemplate;
-
- _this.template = new Template({
- tag: 'div',
-
- attributes: {
- class: ['ck-reset', 'ck-dropdown__panel', bind.if('isVisible', 'ck-dropdown__panel-visible')]
- }
- });
+// Walk forward or backward (depends on `lookBack` flag), node by node as long as they have the same `linkHref` attribute value
+// and return position just before or after (depends on `lookBack` flag) last matched node.
+//
+// @param {engine.model.Position} position Start position.
+// @param {String} value `linkHref` attribute value.
+// @param {Boolean} lookBack Whether walk direction is forward `false` or backward `true`.
+// @returns {engine.model.Position} Position just before last matched node.
+function _findBound(position, value, lookBack) {
+ // Get node before or after position (depends on `lookBack` flag).
+ // When position is inside text node then start searching from text node.
+ var node = position.textNode || (lookBack ? position.nodeBefore : position.nodeAfter);
- _this.register('content', function (el) {
- return el;
- });
+ var lastNode = null;
- /**
- * Controls whether the panel is visible.
- *
- * @observable
- * @member {Boolean} ui.dropdown.DropdownPanelView#isVisible
- */
- return _this;
- }
+ while (node && node.getAttribute('linkHref') == value) {
+ lastNode = node;
+ node = lookBack ? node.previousSibling : node.nextSibling;
+ }
- return DropdownPanelView;
-}(View);
+ return lastNode ? Position.createAt(lastNode, lookBack ? 'before' : 'after') : position;
+}
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -47360,141 +46321,139 @@ var DropdownPanelView = function (_View) {
*/
/**
- * The dropdown controller class.
- *
- * const model = new Model( {
- * label: 'Dropdown',
- * isEnabled: true,
- * inOn: false
- * } );
- *
- * // An instance of Dropdown.
- * new Dropdown( model, new DropdownView() );
- *
- * See {@link ui.dropdown.DropdownView}.
+ * The link command. It is used by the {@link Link.Link link feature}.
*
- * @memberOf ui.dropdown
- * @extends ui.Controller
+ * @memberOf link
+ * @extends core.command.Command
*/
-var Dropdown = function (_Controller) {
- inherits(Dropdown, _Controller);
+var LinkCommand = function (_Command) {
+ inherits(LinkCommand, _Command);
/**
- * Creates an instance of {@link ui.dropdown.Dropdown} class.
- *
- * @param {ui.dropdown.DropdownModel} model Model of this dropdown.
- * @param {ui.View} view View of this dropdown.
+ * @see core.command.Command
+ * @param {core.editor.Editor} editor
*/
- function Dropdown(model, view) {
- classCallCheck(this, Dropdown);
+ function LinkCommand(editor) {
+ classCallCheck(this, LinkCommand);
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(Dropdown).call(this, model, view));
+ /**
+ * Currently selected `linkHref` attribute value.
+ *
+ * @observable
+ * @member {Boolean} core.command.ToggleAttributeCommand#value
+ */
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LinkCommand).call(this, editor));
- _this.addCollection('main');
- _this._createButton();
- _this._createPanel();
+ _this.set('value', undefined);
+
+ _this.listenTo(_this.editor.document.selection, 'change:attribute', function () {
+ _this.value = _this.editor.document.selection.getAttribute('linkHref');
+ });
return _this;
}
/**
- * Creates {@link ui.dropdown.Dropdown#button} of this dropdown.
+ * Checks if {@link engine.model.Document#schema} allows to create attribute in {@link engine.model.Document#selection}
*
* @protected
+ * @returns {Boolean}
*/
- createClass(Dropdown, [{
- key: '_createButton',
- value: function _createButton() {
- var model = this.model;
- var view = this.view;
- var buttonModel = new Model();
-
- // Button needs a separate Model because otherwise it would fire #execute event
- // on the model shared between multiple dropdowns (they would all open at the same time).
- buttonModel.bind('label', 'isOn', 'isEnabled', 'withText').to(model);
-
- /**
- * Button of this dropdown.
- *
- * @readonly
- * @member {ui.button.Button} ui.dropdown.Dropdown#button
- */
- this.add('main', this.button = new Button(buttonModel, new DropdownButtonView()));
+ createClass(LinkCommand, [{
+ key: '_checkEnabled',
+ value: function _checkEnabled() {
+ var document = this.editor.document;
- // When ui.dropdown.Dropdown#button is clicked switch the open/closed state of the Dropdown.
- this.listenTo(buttonModel, 'execute', function () {
- return view.isOpen = !view.isOpen;
- });
+ return isAttributeAllowedInSelection('linkHref', document.selection, document.schema);
}
/**
- * Creates {@link ui.dropdown.Dropdown#panel} of this dropdown.
+ * Executes the command.
+ *
+ * When selection is non-collapsed, then `linkHref` attribute will be applied to nodes inside selection, but only to
+ * those nodes where `linkHref` attribute is allowed (disallowed nodes will be omitted).
+ *
+ * When selection is collapsed and is not inside text with `linkHref` attribute, then new {@link engine.model.Text Text node} with
+ * `linkHref` attribute will be inserted in place of caret, but only if such an element is allowed in this place. `_data` of
+ * the inserted text will equal `href` parameter. Selection will be updated to wrap just inserted text node.
+ *
+ * When selection is collapsed and inside text with `linkHref` attribute, the attribute value will be updated.
*
* @protected
+ * @param {String} href Link destination.
*/
}, {
- key: '_createPanel',
- value: function _createPanel() {
- var panelModel = new Model();
-
- panelModel.bind('isVisible').to(this.view, 'isOpen');
+ key: '_doExecute',
+ value: function _doExecute(href) {
+ var document = this.editor.document;
+ var selection = document.selection;
- /**
- * Panel of this dropdown.
- *
- * @readonly
- * @member {ui.dropdown.DropdownPanel} ui.dropdown.Dropdown#panel
- */
- this.add('main', this.panel = new DropdownPanel(panelModel, new DropdownPanelView()));
- }
- }]);
- return Dropdown;
-}(Controller);
+ document.enqueueChanges(function () {
+ // Keep it as one undo step.
+ var batch = document.batch();
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ // If selection is collapsed then update selected link or insert new one at the place of caret.
+ if (selection.isCollapsed) {
+ var position = selection.getFirstPosition();
+ var parent = position.parent;
-/**
- * The list item controller class.
- *
- * @memberOf ui.list
- * @extends ui.Controller
- */
+ // When selection is inside text with `linkHref` attribute.
+ if (selection.hasAttribute('linkHref')) {
+ // Then update `linkHref` value.
+ var linkRange = findLinkRange(selection.getFirstPosition(), selection.getAttribute('linkHref'));
-var ListItem = function (_Controller) {
- inherits(ListItem, _Controller);
+ batch.setAttribute(linkRange, 'linkHref', href);
- /**
- * Creates an instance of {@link ui.list.ListItem} class.
- *
- * @param {ui.list.ListItemModel} model Model of this list item.
- * @param {ui.View} view View of this list item.
- */
- function ListItem(model, view) {
- classCallCheck(this, ListItem);
+ // Create new range wrapping changed link.
+ selection.setRanges([linkRange]);
+ }
+ // If not then insert text node with `linkHref` attribute in place of caret.
+ else if (document.schema.check({ name: '$text', attributes: 'linkHref', inside: parent.name })) {
+ var node = new Text$1(href, { linkHref: href });
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListItem).call(this, model, view));
+ batch.insert(position, node);
- view.bind('label').to(model);
+ // Create new range wrapping created node.
+ selection.setRanges([Range$1.createOn(node)]);
+ }
+ } else {
+ // If selection has non-collapsed ranges, we change attribute on nodes inside those ranges
+ // omitting nodes where `linkHref` attribute is disallowed.
+ var ranges = getSchemaValidRanges('linkHref', selection.getRanges(), document.schema);
- if (model.style) {
- view.bind('style').to(model);
- }
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- // TODO: Event delegation with altered event name.
- view.on('click', function () {
- return model.fire('execute');
- });
- return _this;
- }
+ try {
+ for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var range = _step.value;
- return ListItem;
-}(Controller);
+ batch.setAttribute(range, 'linkHref', href);
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ }
+ });
+ }
+ }]);
+ return LinkCommand;
+}(Command);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -47502,68 +46461,97 @@ var ListItem = function (_Controller) {
*/
/**
- * The list item view class.
- *
- * See {@link ui.list.ListItem}.
+ * The unlink command. It is used by the {@link Link.Link link feature}.
*
- * @memberOf ui.list
- * @extends ui.View
+ * @memberOf link
+ * @extends core.command.Command
*/
-var ListItemView = function (_View) {
- inherits(ListItemView, _View);
+var UnlinkCommand = function (_Command) {
+ inherits(UnlinkCommand, _Command);
/**
- * @inheritDoc
+ * @see core.command.Command
+ * @param {core.editor.Editor} editor
*/
- function ListItemView() {
- classCallCheck(this, ListItemView);
+ function UnlinkCommand(editor) {
+ classCallCheck(this, UnlinkCommand);
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListItemView).call(this));
+ // Checks when command should be enabled or disabled.
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(UnlinkCommand).call(this, editor));
- var bind = _this.bindTemplate;
+ _this.listenTo(editor.document.selection, 'change:attribute', function () {
+ return _this.refreshState();
+ });
+ return _this;
+ }
- _this.template = new Template({
- tag: 'li',
+ /**
+ * Executes the command.
+ *
+ * When the selection is collapsed, removes `linkHref` attribute from each node with the same `linkHref` attribute value.
+ * When the selection is non-collapsed, removes `linkHref` from each node in selected ranges.
+ *
+ * @protected
+ */
- attributes: {
- class: ['ck-list__item'],
- style: bind.to('style')
- },
- children: [{
- text: bind.to('label')
- }],
+ createClass(UnlinkCommand, [{
+ key: '_doExecute',
+ value: function _doExecute() {
+ var document = this.editor.document;
+ var selection = document.selection;
- on: {
- click: bind.to('click')
- }
- });
+ document.enqueueChanges(function () {
+ // Get ranges to unlink.
+ var rangesToUnlink = selection.isCollapsed ? [findLinkRange(selection.getFirstPosition(), selection.getAttribute('linkHref'))] : selection.getRanges();
- /**
- * The label of the list item.
- *
- * @observable
- * @member {String} ui.list.ListItemView#label
- */
+ // Keep it as one undo step.
+ var batch = document.batch();
- /**
- * (Optional) The DOM style attribute of the list item.
- *
- * @observable
- * @member {String} ui.list.ListItemView#style
- */
+ // Remove `linkHref` attribute from specified ranges.
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = rangesToUnlink[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var range = _step.value;
+
+ batch.removeAttribute(range, 'linkHref');
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ });
+ }
/**
- * Fired when the list item has been clicked.
+ * Checks if selection has `linkHref` attribute.
*
- * @event ui.list.ListItemView#click
+ * @protected
+ * @returns {Boolean}
*/
- return _this;
- }
- return ListItemView;
-}(View);
+ }, {
+ key: '_checkEnabled',
+ value: function _checkEnabled() {
+ return this.editor.document.selection.hasAttribute('linkHref');
+ }
+ }]);
+ return UnlinkCommand;
+}(Command);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -47571,92 +46559,156 @@ var ListItemView = function (_View) {
*/
/**
- * The list controller class.
- *
- * const itemsCollection = new Collection();
- *
- * itemsCollection.add( new Model( { label: 'foo' } ) );
- * itemsCollection.add( new Model( { label: 'bar' } ) );
- *
- * const model = new Model( {
- * items: itemsCollection
- * } );
- *
- * // An instance of List filled up with the `itemsCollection`.
- * // Any change to `itemsCollection` will be reflected in DOM.
- * new List( model, new ListView() );
+ * The link engine feature.
*
- * See {@link ui.list.ListView}, {@link ui.list.ListItem}.
+ * It introduces the `linkHref="url"` attribute in the model which renders to the view as a `` element.
*
- * @memberOf ui.list
- * @extends ui.Controller
+ * @memberOf link
+ * @extends core.Feature
*/
-var List = function (_Controller) {
- inherits(List, _Controller);
+var LinkEngine = function (_Feature) {
+ inherits(LinkEngine, _Feature);
- /**
- * Creates an instance of {@link ui.list.List} class.
- *
- * @param {ui.list.ListModel} model Model of this list.
- * @param {ui.View} view View of this list.
+ function LinkEngine() {
+ classCallCheck(this, LinkEngine);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(LinkEngine).apply(this, arguments));
+ }
+
+ createClass(LinkEngine, [{
+ key: 'init',
+
+ /**
+ * @inheritDoc
*/
- function List(model, view) {
- classCallCheck(this, List);
+ value: function init() {
+ var editor = this.editor;
+ var data = editor.data;
+ var editing = editor.editing;
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(List).call(this, model, view));
+ // Allow link attribute on all inline nodes.
+ editor.document.schema.allow({ name: '$inline', attributes: 'linkHref' });
- var list = _this.addCollection('list');
+ // Build converter from model to view for data and editing pipelines.
+ buildModelConverter().for(data.modelToView, editing.modelToView).fromAttribute('linkHref').toElement(function (linkHref) {
+ return new LinkElement('a', { href: linkHref });
+ });
- list.bind(model.items).as(ListItem, ListItemView);
- list.delegate('execute').to(model);
- return _this;
- }
+ // Build converter from view to model for data pipeline.
+ buildViewConverter().for(data.viewToModel).fromElement('a').toAttribute(function (viewElement) {
+ return {
+ key: 'linkHref',
+ value: viewElement.getAttribute('href')
+ };
+ });
- return List;
-}(Controller);
+ // Create linking commands.
+ editor.commands.set('link', new LinkCommand(editor));
+ editor.commands.set('unlink', new UnlinkCommand(editor));
+ }
+ }]);
+ return LinkEngine;
+}(Feature);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
+/* global document */
+
/**
- * The list view class.
+ * Handles click outside of specified element and fires action.
*
- * See {@link ui.list.List}.
+ * Note that it is not handled by a `click` event, this is to avoid situation when click on some trigger
+ * opens and closes element at the same time.
*
- * @memberOf ui.list
- * @extends ui.View
+ * @param {Object} [options] Configuration options.
+ * @param {ui.Controller} [options.controller] The controller to which this behavior should be added.
+ * @param {utils.Observable} [options.model] Used together with `options.activeIf` to know when to listen for clicks.
+ * @param {String} [options.activeIf] Used together with `options.model` to know when to listen for clicks.
+ * @param {HTMLElement} [options.contextElement] Target element, click on it will not fire callback.
+ * @param {Function} [options.callback] Function fired after clicking outside of specified element.
*/
+function clickOutsideHandler(options) {
+ var controller = options.controller;
+ var clickHandler = function clickHandler(evt, domEvt) {
+ return handleClickOutside(domEvt.target, options.contextElement, options.callback);
+ };
-var ListView = function (_View) {
- inherits(ListView, _View);
+ controller.listenTo(options.model, 'change:' + options.activeIf, function (evt, name, value) {
+ if (value) {
+ controller.listenTo(document, 'mouseup', clickHandler);
+ } else {
+ controller.stopListening(document, 'mouseup', clickHandler);
+ }
+ });
- /**
- * @inheritDoc
- */
- function ListView() {
- classCallCheck(this, ListView);
+ // When `activeIf` property is `true` on init.
+ if (options.model[options.activeIf]) {
+ controller.listenTo(document, 'mouseup', clickHandler);
+ }
+}
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListView).call(this));
+// Fires callback when clicked element is outside of context element.
+//
+// @private
+// @param {HTMLElement} clickedElement Clicked element.
+// @param {HTMLElement} contextElement Click on this element will not fire callback.
+// @param {Function} callback Action fired after clicking outside of context element.
+function handleClickOutside(clickedElement, contextElement, callback) {
+ if (!contextElement.contains(clickedElement)) {
+ callback();
+ }
+}
- _this.template = new Template({
- tag: 'ul',
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- attributes: {
- class: ['ck-reset', 'ck-list']
- }
- });
+/* global document */
- _this.register('list', function (el) {
- return el;
- });
- return _this;
+/**
+ * Handles Esc keydown and fires action.
+ *
+ * @param {Object} [options] Configuration options.
+ * @param {ui.Controller} [options.controller] The controller to which this behavior should be added.
+ * @param {utils.Observable} [options.model] Used together with `options.activeIf` to know when to listen for keydown.
+ * @param {String} [options.activeIf] Used together with `options.model` to know when to listen for keydown.
+ * @param {Function} [options.callback] Function fired after Esc is pressed.
+ * @returns {Function} Click handler
+ */
+function escPressHandler(options) {
+ var controller = options.controller;
+ var keypressHandler = function keypressHandler(evt, domEvt) {
+ return handleEscPress(domEvt.keyCode, options.callback);
+ };
+
+ controller.listenTo(options.model, 'change:' + options.activeIf, function (evt, name, value) {
+ if (value) {
+ controller.listenTo(document, 'keydown', keypressHandler);
+ } else {
+ controller.stopListening(document, 'keydown', keypressHandler);
+ }
+ });
+
+ // When `activeIf` property is `true` on init.
+ if (options.model[options.activeIf]) {
+ controller.listenTo(document, 'keydown', keypressHandler);
}
+}
- return ListView;
-}(View);
+// Fires callback when ESC key was pressed.
+//
+// @private
+// @param {HTMLElement} keyCode Code of pressed key.
+// @param {Function} callback Action fired after ESC press.
+function handleEscPress(keyCode, callback) {
+ if (keyCode == keyCodes.esc) {
+ callback();
+ }
+}
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -47664,328 +46716,444 @@ var ListView = function (_View) {
*/
/**
- * The list dropdown controller class. It represents a dropdown
- * with a {@link ui.list.List} component.
- *
- * const model = new Model( {
- * label: 'List Dropdown',
- * isEnabled: true,
- * isOn: false,
- * content: {@link ui.dropdown.list.ListDropdownModel#content}
- * } );
+ * The balloon panel controller class.
*
- * // An instance of Dropdown.
- * new ListDropdown( model, new ListDropdownView() );
+ * new BalloonPanel( new Model(), new BalloonPanelView() );
*
- * See {@link ui.dropdown.list.ListDropdownView}.
+ * See {@link ui.balloonPanel.BalloonPanelView}.
*
- * @memberOf ui.dropdown.list
- * @extends ui.dropdown.Dropdown
+ * @memberOf ui.balloonPanel
+ * @extends ui.Controller
*/
-var ListDropdown = function (_Dropdown) {
- inherits(ListDropdown, _Dropdown);
+var BalloonPanel = function (_Controller) {
+ inherits(BalloonPanel, _Controller);
/**
- * Creates an instance of {@link ui.dropdown.list.ListDropdown} class.
- *
- * @param {ui.dropdown.list.ListDropdownModel} model Model of this list dropdown.
- * @param {ui.View} view View of this list dropdown.
- */
- function ListDropdown(model, view) {
- classCallCheck(this, ListDropdown);
-
- /**
- * List of this list dropdown.
- *
- * @readonly
- * @member {ui.list.List} ui.dropdown.list.ListDropdown#list
- */
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListDropdown).call(this, model, view));
-
- _this.list = new List(_this.model.content, new ListView());
+ * Creates an instance of {@link ui.balloonPanel.BalloonPanel} class.
+ *
+ * @param {ui.balloonPanel.BalloonPanelModel} model Model of this balloon panel.
+ * @param {ui.View} view View of this balloon panel.
+ */
+ function BalloonPanel(model, view) {
+ classCallCheck(this, BalloonPanel);
- // Delegate ui.list.ListModel#execute to the model.
- _this.model.content.delegate('execute').to(model);
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(BalloonPanel).call(this, model, view));
- // Collapse the dropdown when an item in the panel is clicked.
- _this.listenTo(model, 'execute', function () {
- view.isOpen = false;
+ view.set({
+ top: 0,
+ left: 0,
+ arrow: 'se',
+ isVisible: false
});
- _this.panel.add('content', _this.list);
+ view.bind('maxWidth').to(model);
+
+ _this.addCollection('content');
return _this;
}
- return ListDropdown;
-}(Dropdown);
+ return BalloonPanel;
+}(Controller);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
+/* globals window, document, Range, HTMLElement */
+
+var toPx$1 = toUnit('px');
+var arrowLeftOffset = 30;
+var arrowTopOffset = 15;
+
/**
- * The dropdown view class.
+ * The balloon panel view class.
*
- * See {@link ui.dropdown.Dropdown}.
+ * See {@link ui.balloonPanel.BalloonPanel}.
*
- * @memberOf ui.dropdown
+ * @memberOf ui.balloonPanel
* @extends ui.View
*/
-var DropdownView = function (_View) {
- inherits(DropdownView, _View);
+var BalloonPanelView = function (_View) {
+ inherits(BalloonPanelView, _View);
/**
* @inheritDoc
*/
- function DropdownView() {
- classCallCheck(this, DropdownView);
+ function BalloonPanelView(locale) {
+ classCallCheck(this, BalloonPanelView);
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DropdownView).call(this));
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(BalloonPanelView).call(this, locale));
- _this.set('isOpen', false);
+ var bind = _this.bindTemplate;
_this.template = new Template({
tag: 'div',
-
attributes: {
- class: ['ck-dropdown']
+ class: ['ck-balloon-panel', bind.to('arrow', function (value) {
+ return 'ck-balloon-panel_arrow_' + value;
+ }), bind.if('isVisible', 'ck-balloon-panel_visible')],
+
+ style: {
+ top: bind.to('top', toPx$1),
+ left: bind.to('left', toPx$1),
+ maxWidth: bind.to('maxWidth', toPx$1)
+ },
+
+ // Make this element `focusable` to be available for adding to FocusTracker.
+ tabindex: -1
}
});
- _this.register('main', function (el) {
+ _this.register('content', function (el) {
return el;
});
/**
- * Controls whether the dropdown view is open, which also means its
- * {@link ui.dropdown.Dropdown#panel} is visible.
+ * The absolute top position of the balloon panel in pixels.
*
* @observable
- * @member {Boolean} ui.dropdown.DropdownViewl#isOpen
+ * @member {Number} ui.balloonPanel.BalloonPanelView#top
*/
- return _this;
- }
-
- return DropdownView;
-}(View);
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ /**
+ * The absolute left position of the balloon panel in pixels.
+ *
+ * @observable
+ * @member {Number} ui.balloonPanel.BalloonPanelView#left
+ */
-/* globals document */
+ /**
+ * The maximum width of the balloon panel, as in CSS.
+ *
+ * @observable
+ * @member {Number} ui.balloonPanel.BalloonPanelView#maxWidth
+ */
-/**
- * The list dropdown view class.
- *
- * See {@link ui.dropdown.list.ListDropdown}.
- *
- * @memberOf ui.dropdown.list
- * @extends ui.dropdown.DropdownView
- */
+ /**
+ * Balloon panel arrow direction.
+ *
+ * @observable
+ * @member {'se'|'sw'|'ne'|'nw'} ui.balloonPanel.BalloonPanelView#arrow
+ */
-var ListDropdownView = function (_DropdownView) {
- inherits(ListDropdownView, _DropdownView);
+ /**
+ * Controls whether the balloon panel is visible or not.
+ *
+ * @observable
+ * @member {Boolean} ui.balloonPanel.BalloonPanelView#isVisible
+ */
+ return _this;
+ }
/**
- * @inheritDoc
+ * Shows the balloon panel.
+ *
+ * See {@link ui.balloonPanel.BalloonPanelView#isVisible}.
*/
- function ListDropdownView() {
- classCallCheck(this, ListDropdownView);
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListDropdownView).call(this));
- _this.listenTo(_this, 'change:isOpen', function (evt, name, value) {
- if (value) {
- // TODO: It will probably be focus/blur-based rather than click. It should be bound
- // to focusmanager of some sort.
- _this.listenTo(document, 'click', function (evtInfo, _ref) {
- var domEvtTarget = _ref.target;
+ createClass(BalloonPanelView, [{
+ key: 'show',
+ value: function show() {
+ this.isVisible = true;
+ }
- // Collapse the dropdown when the webpage outside of the component is clicked.
- if (_this.element != domEvtTarget && !_this.element.contains(domEvtTarget)) {
- _this.isOpen = false;
- }
- });
- } else {
- _this.stopListening(document);
- }
- });
- return _this;
- }
+ /**
+ * Hides the balloon panel.
+ *
+ * See {@link ui.balloonPanel.BalloonPanelView#isVisible}.
+ */
- return ListDropdownView;
-}(DropdownView);
+ }, {
+ key: 'hide',
+ value: function hide() {
+ this.isVisible = false;
+ }
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ /**
+ * Attaches the balloon panel to a specified DOM element or range with a smart heuristics.
+ *
+ * **Notes**:
+ *
+ * * The algorithm takes the geometry of the "limiter element" into consideration so,
+ * if possible, the balloon is positioned within the rect of that element.
+ * * If possible, the balloon is positioned within the area of the "limiter element"
+ * fitting into the browser viewport visible to the user. It prevents the panel from
+ * appearing off screen.
+ *
+ * The heuristics chooses from among 4 available positions relative to the target DOM element or range:
+ *
+ * * South east:
+ *
+ * [ Target ]
+ * ^
+ * +-----------------+
+ * | |
+ * +-----------------+
+ *
+ *
+ * * South west:
+ *
+ * [ Target ]
+ * ^
+ * +-----------------+
+ * | |
+ * +-----------------+
+ *
+ *
+ * * North east:
+ *
+ * +-----------------+
+ * | |
+ * +-----------------+
+ * V
+ * [ Target ]
+ *
+ *
+ * * North west:
+ *
+ * +-----------------+
+ * | |
+ * +-----------------+
+ * V
+ * [ Target ]
+ *
+ * See {@ link ui.balloonPanel.BalloonPanelView#arrow}.
+ *
+ * @param {HTMLElement|Range} elementOrRange Target DOM element or range to which the balloon will be attached.
+ * @param {HTMLElement|Object} limiterElementOrRect The DOM element or element rect beyond which area the balloon panel should not be
+ * positioned, if possible.
+ */
-/**
- * The headings feature. It introduces the `headings` drop-down list and the `heading` command which allow
- * to convert paragraphs into headings.
- *
- * @memberOf heading
- * @extends core.Feature
- */
+ }, {
+ key: 'attachTo',
+ value: function attachTo(elementOrRange, limiterElementOrRect) {
+ this.show();
-var Heading = function (_Feature) {
- inherits(Heading, _Feature);
+ var elementOrRangeRect = new AbsoluteDomRect(elementOrRange);
+ var panelRect = new AbsoluteDomRect(this.element);
+ var limiterVisibleRect = getAbsoluteRectVisibleInTheViewport(limiterElementOrRect);
- function Heading() {
- classCallCheck(this, Heading);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Heading).apply(this, arguments));
- }
+ // Create a rect for each of the possible balloon positions and feed them to _smartAttachTo,
+ // which will use whichever is the optimal. Position are ordered from most to less desired.
+ var possiblePanelRects = {
+ // The absolute rect for "South east" position.
+ se: panelRect.clone().moveTo({
+ top: elementOrRangeRect.bottom + arrowTopOffset,
+ left: elementOrRangeRect.left + elementOrRangeRect.width / 2 - arrowLeftOffset
+ }),
+
+ // The absolute rect for "South west" position.
+ sw: panelRect.clone().moveTo({
+ top: elementOrRangeRect.bottom + arrowTopOffset,
+ left: elementOrRangeRect.left + elementOrRangeRect.width / 2 - panelRect.width + arrowLeftOffset
+ }),
+
+ // The absolute rect for "North east" position.
+ ne: panelRect.clone().moveTo({
+ top: elementOrRangeRect.top - panelRect.height - arrowTopOffset,
+ left: elementOrRangeRect.left + elementOrRangeRect.width / 2 - arrowLeftOffset
+ }),
- createClass(Heading, [{
- key: 'init',
+ // The absolute rect for "North west" position.
+ nw: panelRect.clone().moveTo({
+ top: elementOrRangeRect.top - panelRect.height - arrowTopOffset,
+ left: elementOrRangeRect.left + elementOrRangeRect.width / 2 - panelRect.width + arrowLeftOffset
+ })
+ };
+ this._smartAttachTo(possiblePanelRects, limiterVisibleRect, panelRect.width * panelRect.height);
+ }
/**
- * @inheritDoc
+ * For the given set of possible rects, chooses the one which fits the best into both - browser viewport and
+ * `visibleContainerRect`, which is when their intersection has the biggest area. Note that priority is a possible
+ * highest intersection area with browser viewport.
+ *
+ * @private
+ * @param {Object} rects Set of positions where balloon can be placed.
+ * @param {AbsoluteDomRect} visibleContainerRect The absolute rect of the visible part of container element.
+ * @param {Number} panelSurfaceArea Panel surface area.
*/
- value: function init() {
- var editor = this.editor;
- var command = editor.commands.get('heading');
- var formats = command.formats;
- var collection = new Collection();
- // Add formats to collection.
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+ }, {
+ key: '_smartAttachTo',
+ value: function _smartAttachTo(rects, visibleContainerRect, panelSurfaceArea) {
+ var viewportRect = new AbsoluteDomRect(getAbsoluteViewportRect());
+ var maxIntersectRectPos = void 0;
+ var maxContainerIntersectArea = -1;
+ var maxViewportIntersectArea = -1;
- try {
- for (var _iterator = formats[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var format = _step.value;
+ // Find the best place. Stop searching when the position with fully visible panel has been found.
+ Object.keys(rects).some(function (rectPos) {
+ var containerIntersectArea = rects[rectPos].getIntersectArea(visibleContainerRect);
+ var viewportIntersectArea = rects[rectPos].getIntersectArea(viewportRect);
- collection.add(new Model({
- id: format.id,
- label: format.label
- }));
+ if (viewportIntersectArea >= maxViewportIntersectArea && containerIntersectArea > maxContainerIntersectArea) {
+ maxIntersectRectPos = rectPos;
+ maxContainerIntersectArea = containerIntersectArea;
+ maxViewportIntersectArea = viewportIntersectArea;
}
- // Create dropdown model.
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
+ return maxContainerIntersectArea === panelSurfaceArea;
+ });
- var dropdownModel = new Model({
- isEnabled: true,
- isOn: false,
- label: 'Heading',
- withText: true,
+ // Move the balloon panel.
+ this.arrow = maxIntersectRectPos;
+ this.top = rects[maxIntersectRectPos].top;
+ this.left = rects[maxIntersectRectPos].left;
+ }
+ }]);
+ return BalloonPanelView;
+}(View);
- // Create item list model.
- content: new Model({
- items: collection
- })
- });
+var AbsoluteDomRect = function () {
+ // Create instance of AbsoluteDomRect class.
+ //
+ // @param {HTMLElement|Range|Object} elementOrRangeOrRect Source object to create the rect.
+ function AbsoluteDomRect(elementOrRangeOrRect) {
+ classCallCheck(this, AbsoluteDomRect);
- // Bind dropdown model to command.
- dropdownModel.bind('isEnabled').to(command, 'isEnabled');
- dropdownModel.bind('label').to(command, 'value', function (format) {
- return format.label;
- });
+ Object.assign(this, getAbsoluteRect(elementOrRangeOrRect));
+ }
- // Execute command when an item from the dropdown is selected.
- this.listenTo(dropdownModel, 'execute', function (evt) {
- editor.execute('heading', { formatId: evt.source.id });
- editor.editing.view.focus();
- });
+ // Clone instance of this class.
+ //
+ // @returns {AbsoluteDomRect}
- // Register UI component.
- editor.ui.featureComponents.add('headings', ListDropdown, ListDropdownView, dropdownModel);
+
+ createClass(AbsoluteDomRect, [{
+ key: 'clone',
+ value: function clone() {
+ return new AbsoluteDomRect(this);
}
- }], [{
- key: 'requires',
- /**
- * @inheritDoc
- */
- get: function get() {
- return [HeadingEngine];
+ // Move current box to specified position.
+ //
+ // @param {Number} top New to position.
+ // @param {Number} left New left position.
+ // @returns {AbsoluteDomRect}
+
+ }, {
+ key: 'moveTo',
+ value: function moveTo(_ref) {
+ var top = _ref.top;
+ var left = _ref.left;
+
+ this.top = top;
+ this.right = left + this.width;
+ this.bottom = top + this.height;
+ this.left = left;
+
+ return this;
+ }
+
+ // Get intersect surface area of this AbsoluteDomRect and other AbsoluteDomRect.
+ //
+ // @param {AbsoluteDomRect} rect
+ // @returns {Number} Overlap surface area.
+
+ }, {
+ key: 'getIntersectArea',
+ value: function getIntersectArea(rect) {
+ var hOverlap = Math.max(0, Math.min(this.right, rect.right) - Math.max(this.left, rect.left));
+ var vOverlap = Math.max(0, Math.min(this.bottom, rect.bottom) - Math.max(this.top, rect.top));
+
+ return hOverlap * vOverlap;
}
}]);
- return Heading;
-}(Feature);
+ return AbsoluteDomRect;
+}();
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+// Returns the client rect of an HTMLElement, Range, or rect. The obtained geometry of the rect
+// corresponds with `position: absolute` relative to the `` (`document.body`).
+//
+// @private
+// @param {HTMLElement|Range|Object} elementOrRangeOrRect Target object witch rect is to be determined.
+// @returns {Object} Client rect object.
-/**
- * {@link engine.view.Document#click Click} event observer.
- *
- * Note that this observer is not available by default. To make it available it needs to be added to {@link engine.view.Document}
- * by a {@link engine.view.Document#addObserver} method.
- *
- * @memberOf engine.view.observer
- * @extends engine.view.observer.DomEventObserver
- */
-var ClickObserver = function (_DomEventObserver) {
- inherits(ClickObserver, _DomEventObserver);
+function getAbsoluteRect(elementOrRangeOrRect) {
+ var bodyRect = document.body.getBoundingClientRect();
- function ClickObserver(document) {
- classCallCheck(this, ClickObserver);
+ if (elementOrRangeOrRect instanceof HTMLElement || elementOrRangeOrRect instanceof Range) {
+ var elementRect = elementOrRangeOrRect.getBoundingClientRect();
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ClickObserver).call(this, document));
+ return {
+ top: elementRect.top - bodyRect.top,
+ right: elementRect.right - bodyRect.left,
+ bottom: elementRect.bottom - bodyRect.top,
+ left: elementRect.left - bodyRect.left,
+ width: elementRect.width,
+ height: elementRect.height
+ };
+ }
- _this.domEventType = 'click';
- return _this;
- }
+ // The rect has been passed.
+ var absoluteRect = Object.assign({}, elementOrRangeOrRect);
- createClass(ClickObserver, [{
- key: 'onDomEvent',
- value: function onDomEvent(domEvent) {
- this.fire(domEvent.type, domEvent);
- }
- }]);
- return ClickObserver;
-}(DomEventObserver);
+ if (absoluteRect.width === undefined) {
+ absoluteRect.width = absoluteRect.right - absoluteRect.left;
+ }
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ if (absoluteRect.height === undefined) {
+ absoluteRect.height = absoluteRect.bottom - absoluteRect.top;
+ }
-/**
- * This class is to mark specific {@link engine.view.Node} as {@link link.LinkElement}.
- * E.g. There could be a situation when different features will create nodes with the same names,
- * and hence they must be identified somehow.
- *
- * @memberOf link
- * @extends engine.view.AttributeElement
- */
+ return absoluteRect;
+}
-var LinkElement = function (_AttributeElement) {
- inherits(LinkElement, _AttributeElement);
+// Returns the client rect of the element limited by the visible (to the user)
+// viewport of the browser window.
+//
+// [Browser viewport]
+// +---------------------------------------+
+// | [Element] |
+// | +----------------------+
+// | |##############| |
+// | |##############| |
+// | |#######^######| |
+// | +-------|--------------+
+// | | |
+// +--------------------------------|------+
+// |
+// \- [Element rect visible in the viewport]
+//
+// @private
+// @param {HTMLElement|Object} element Object which visible area rect is to be determined.
+// @returns {AbsoluteDomRect} An absolute rect of the area visible in the viewport.
+function getAbsoluteRectVisibleInTheViewport(element) {
+ var elementRect = getAbsoluteRect(element);
+ var viewportRect = getAbsoluteViewportRect();
+
+ return new AbsoluteDomRect({
+ top: Math.max(elementRect.top, viewportRect.top),
+ left: Math.max(elementRect.left, viewportRect.left),
+ right: Math.min(elementRect.right, viewportRect.right),
+ bottom: Math.min(elementRect.bottom, viewportRect.bottom)
+ });
+}
- function LinkElement() {
- classCallCheck(this, LinkElement);
- return possibleConstructorReturn(this, Object.getPrototypeOf(LinkElement).apply(this, arguments));
- }
+// Get browser viewport rect.
+//
+// @private
+// @returns {Object} Viewport rect.
+function getAbsoluteViewportRect() {
+ var windowScrollX = window.scrollX;
+ var windowScrollY = window.scrollY;
+ var windowWidth = window.innerWidth;
+ var windowHeight = window.innerHeight;
- return LinkElement;
-}(AttributeElement);
+ return {
+ top: windowScrollY,
+ right: windowWidth + windowScrollX,
+ bottom: windowHeight + windowScrollY,
+ left: windowScrollX
+ };
+}
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -47993,38 +47161,41 @@ var LinkElement = function (_AttributeElement) {
*/
/**
- * Walk backward and forward from start position, node by node as long as they have the same `linkHref` attribute value and return
- * {@link engine.model.Range Range} with found link.
+ * The label controller class.
*
- * @param {engine.model.Position} position Start position.
- * @param {String} value `linkHref` attribute value.
- * @returns {engine.model.Range} Link range.
+ * const model = new Model( {
+ * text: 'Label of some input.',
+ * for: 'id-of-related-input'
+ * } );
+ *
+ * new Label( model, new LabelView() );
+ *
+ * See {@link ui.input.LabelView}.
+ *
+ * @memberOf ui.label
+ * @extends ui.Controller
*/
-function findLinkRange(position, value) {
- return new Range$1(_findBound(position, value, true), _findBound(position, value, false));
-}
-// Walk forward or backward (depends on `lookBack` flag), node by node as long as they have the same `linkHref` attribute value
-// and return position just before or after (depends on `lookBack` flag) last matched node.
-//
-// @param {engine.model.Position} position Start position.
-// @param {String} value `linkHref` attribute value.
-// @param {Boolean} lookBack Whether walk direction is forward `false` or backward `true`.
-// @returns {engine.model.Position} Position just before last matched node.
-function _findBound(position, value, lookBack) {
- // Get node before or after position (depends on `lookBack` flag).
- // When position is inside text node then start searching from text node.
- var node = position.textNode || (lookBack ? position.nodeBefore : position.nodeAfter);
+var Label = function (_Controller) {
+ inherits(Label, _Controller);
- var lastNode = null;
+ /**
+ * Creates an instance of {@link ui.label.Label} class.
+ *
+ * @param {ui.label.LabelModel} model Model of this label.
+ * @param {ui.View} view View of this label.
+ */
+ function Label(model, view) {
+ classCallCheck(this, Label);
- while (node && node.getAttribute('linkHref') == value) {
- lastNode = node;
- node = lookBack ? node.previousSibling : node.nextSibling;
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(Label).call(this, model, view));
+
+ view.bind('text', 'for').to(model);
+ return _this;
}
- return lastNode ? Position.createAt(lastNode, lookBack ? 'before' : 'after') : position;
-}
+ return Label;
+}(Controller);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -48032,237 +47203,116 @@ function _findBound(position, value, lookBack) {
*/
/**
- * The link command. It is used by the {@link Link.Link link feature}.
+ * The labeled input controller class. It contains two components, {@link ui.input.Label Label}
+ * and `InputComponent` instance, connected by an unique id.
*
- * @memberOf link
- * @extends core.command.Command
+ * const input = new InputText( new Model(), new InputTextView() );
+ *
+ * const model = new Model( {
+ * label: 'Label text'
+ * value: 'init value',
+ * } );
+ *
+ * new LabeledInput( model, new LabeledInputView(), input );
+ *
+ * See {@link ui.input.labeled.LabeledInputView}.
+ *
+ * @memberOf ui.input.labeled
+ * @extends ui.Controller
*/
-var LinkCommand = function (_Command) {
- inherits(LinkCommand, _Command);
+var LabeledInput = function (_Controller) {
+ inherits(LabeledInput, _Controller);
/**
- * @see core.command.Command
- * @param {core.editor.Editor} editor
+ * Creates an instance of {@link ui.input.labeled.LabeledInput} class.
+ *
+ * @param {ui.input.labeled.LabeledInputModel} model Model of this input.
+ * @param {ui.View} view View of this input.
+ * @param {Function} InputClass Constructor of the input component.
+ * @param {ui.Model} model Model of the input component.
*/
- function LinkCommand(editor) {
- classCallCheck(this, LinkCommand);
+ function LabeledInput(model, view, InputClass, inputModel) {
+ classCallCheck(this, LabeledInput);
/**
- * Currently selected `linkHref` attribute value.
+ * An unique `id` to pair the input with its label.
*
- * @observable
- * @member {Boolean} core.command.ToggleAttributeCommand#value
+ * @protected
+ * @member {ui.input.Label}
*/
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LinkCommand).call(this, editor));
-
- _this.set('value', undefined);
-
- _this.listenTo(_this.editor.document.selection, 'change:attribute', function () {
- _this.value = _this.editor.document.selection.getAttribute('linkHref');
- });
- return _this;
- }
-
- /**
- * Checks if {@link engine.model.Document#schema} allows to create attribute in {@link engine.model.Document#selection}
- *
- * @protected
- * @returns {Boolean}
- */
-
-
- createClass(LinkCommand, [{
- key: '_checkEnabled',
- value: function _checkEnabled() {
- var document = this.editor.document;
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LabeledInput).call(this, model, view));
- return isAttributeAllowedInSelection('linkHref', document.selection, document.schema);
- }
+ _this._uid = 'ck-input-' + uid();
/**
- * Executes the command.
- *
- * When selection is non-collapsed, then `linkHref` attribute will be applied to nodes inside selection, but only to
- * those nodes where `linkHref` attribute is allowed (disallowed nodes will be omitted).
- *
- * When selection is collapsed and is not inside text with `linkHref` attribute, then new {@link engine.model.Text Text node} with
- * `linkHref` attribute will be inserted in place of caret, but only if such an element is allowed in this place. `_data` of
- * the inserted text will equal `href` parameter. Selection will be updated to wrap just inserted text node.
- *
- * When selection is collapsed and inside text with `linkHref` attribute, the attribute value will be updated.
+ * The `Label` controller instance.
*
- * @protected
- * @param {String} href Link destination.
+ * @member {ui.input.Label}
*/
-
- }, {
- key: '_doExecute',
- value: function _doExecute(href) {
- var document = this.editor.document;
- var selection = document.selection;
-
- document.enqueueChanges(function () {
- // Keep it as one undo step.
- var batch = document.batch();
-
- // If selection is collapsed then update selected link or insert new one at the place of caret.
- if (selection.isCollapsed) {
- var position = selection.getFirstPosition();
- var parent = position.parent;
-
- // When selection is inside text with `linkHref` attribute.
- if (selection.hasAttribute('linkHref')) {
- // Then update `linkHref` value.
- var linkRange = findLinkRange(selection.getFirstPosition(), selection.getAttribute('linkHref'));
-
- batch.setAttribute(linkRange, 'linkHref', href);
-
- // Create new range wrapping changed link.
- selection.setRanges([linkRange]);
- }
- // If not then insert text node with `linkHref` attribute in place of caret.
- else if (document.schema.check({ name: '$text', attributes: 'linkHref', inside: parent.name })) {
- var node = new Text$1(href, { linkHref: href });
-
- batch.insert(position, node);
-
- // Create new range wrapping created node.
- selection.setRanges([Range$1.createOn(node)]);
- }
- } else {
- // If selection has non-collapsed ranges, we change attribute on nodes inside those ranges
- // omitting nodes where `linkHref` attribute is disallowed.
- var ranges = getSchemaValidRanges('linkHref', selection.getRanges(), document.schema);
-
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
-
- try {
- for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var range = _step.value;
-
- batch.setAttribute(range, 'linkHref', href);
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
- }
- });
- }
- }]);
- return LinkCommand;
-}(Command);
-
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * The unlink command. It is used by the {@link Link.Link link feature}.
- *
- * @memberOf link
- * @extends core.command.Command
- */
-
-var UnlinkCommand = function (_Command) {
- inherits(UnlinkCommand, _Command);
-
- /**
- * @see core.command.Command
- * @param {core.editor.Editor} editor
- */
- function UnlinkCommand(editor) {
- classCallCheck(this, UnlinkCommand);
-
- // Checks when command should be enabled or disabled.
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(UnlinkCommand).call(this, editor));
-
- _this.listenTo(editor.document.selection, 'change:attribute', function () {
- return _this.refreshState();
- });
+ _this.label = _this._createLabel();
+
+ /**
+ * The input controller instance.
+ *
+ * @member {ui.input.InputText}
+ */
+ _this.input = _this._spawnInput(InputClass, inputModel);
return _this;
}
/**
- * Executes the command.
- *
- * When the selection is collapsed, removes `linkHref` attribute from each node with the same `linkHref` attribute value.
- * When the selection is non-collapsed, removes `linkHref` from each node in selected ranges.
+ * Returns the input value.
*
- * @protected
+ * @returns {String} Input value.
*/
- createClass(UnlinkCommand, [{
- key: '_doExecute',
- value: function _doExecute() {
- var document = this.editor.document;
- var selection = document.selection;
-
- document.enqueueChanges(function () {
- // Get ranges to unlink.
- var rangesToUnlink = selection.isCollapsed ? [findLinkRange(selection.getFirstPosition(), selection.getAttribute('linkHref'))] : selection.getRanges();
+ createClass(LabeledInput, [{
+ key: '_createLabel',
- // Keep it as one undo step.
- var batch = document.batch();
- // Remove `linkHref` attribute from specified ranges.
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+ /**
+ * Initializes the {@link ui.input.Label Label} instance.
+ *
+ * @private
+ * @returns {Label}
+ */
+ value: function _createLabel() {
+ var model = new Model();
- try {
- for (var _iterator = rangesToUnlink[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var range = _step.value;
+ model.bind('text').to(this.model, 'label');
+ model.set('for', this._uid);
- batch.removeAttribute(range, 'linkHref');
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
- });
+ return new Label(model, this.view.labelView);
}
/**
- * Checks if selection has `linkHref` attribute.
+ * Creates the new input using given constructor and model.
+ * Binds basic attributes of the newly created input's model to {ui.input.labeled.LabeledInputModel}.
*
- * @protected
- * @returns {Boolean}
+ * @private
+ * @param {Function} InputClass A constructor of the input component.
+ * @param {ui.Model} inputModel Model of the input component.
+ * @returns {ui.Controller} An instance of the input component.
*/
}, {
- key: '_checkEnabled',
- value: function _checkEnabled() {
- return this.editor.document.selection.hasAttribute('linkHref');
+ key: '_spawnInput',
+ value: function _spawnInput(InputClass, inputModel) {
+ inputModel.bind('value').to(this.model, 'value');
+ inputModel.set('id', this._uid);
+
+ return new InputClass(inputModel, this.view.inputView);
+ }
+ }, {
+ key: 'value',
+ get: function get() {
+ return this.input.value;
}
}]);
- return UnlinkCommand;
-}(Command);
+ return LabeledInput;
+}(Controller);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -48270,156 +47320,279 @@ var UnlinkCommand = function (_Command) {
*/
/**
- * The link engine feature.
+ * The text input controller class.
*
- * It introduces the `linkHref="url"` attribute in the model which renders to the view as a ` ` element.
+ * const model = new Model( {
+ * value: 'Initial value.',
+ * id: 'id-of-this-input'
+ * } );
*
- * @memberOf link
- * @extends core.Feature
+ * new InputText( model, new InputTextView() );
+ *
+ * See {@link ui.input.InputTextView}.
+ *
+ * @memberOf ui.input
+ * @extends ui.Controller
*/
-var LinkEngine = function (_Feature) {
- inherits(LinkEngine, _Feature);
+var InputText = function (_Controller) {
+ inherits(InputText, _Controller);
- function LinkEngine() {
- classCallCheck(this, LinkEngine);
- return possibleConstructorReturn(this, Object.getPrototypeOf(LinkEngine).apply(this, arguments));
- }
+ /**
+ * Creates an instance of {@link ui.input.InputText} class.
+ *
+ * @param {ui.input.InputTextModel} model Model of this input.
+ * @param {ui.View} view View of this input.
+ */
+ function InputText(model, view) {
+ classCallCheck(this, InputText);
- createClass(LinkEngine, [{
- key: 'init',
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(InputText).call(this, model, view));
+
+ view.bind('value', 'id').to(model);
+ return _this;
+ }
+
+ /**
+ * Returns the input value.
+ *
+ * @returns {String} input value.
+ */
+
+
+ createClass(InputText, [{
+ key: 'value',
+ get: function get() {
+ return this.view.element.value;
+ }
+ }]);
+ return InputText;
+}(Controller);
+
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * The link form class.
+ *
+ * new LinkForm( new Model(), new LinkFormView() );
+ *
+ * See {@link link.ui.LinkFormView}.
+ *
+ * @memberOf link.ui
+ * @extends ui.Controller
+ */
+
+var LinkForm = function (_Controller) {
+ inherits(LinkForm, _Controller);
+
+ /**
+ * Creates an instance of {@link link.ui.LinkForm} class.
+ *
+ * @param {link.ui.LinkFormModel} model Model of this link form.
+ * @param {ui.View} view View of this link form.
+ */
+ function LinkForm(model, view) {
+ classCallCheck(this, LinkForm);
+
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LinkForm).call(this, model, view));
+
+ var t = _this.view.t;
+ var urlInputModel = new Model({
+ label: t('Link URL')
+ });
+
+ // Bind LabeledInputModel#value to LinkFormModel#url.
+ urlInputModel.bind('value').to(model, 'url');
/**
- * @inheritDoc
+ * The URL input inside {@link link.ui.LinkForm}.
+ *
+ * @member {ui.input.labeled.LabeledInput} link.ui.LinkForm#urlInput
*/
- value: function init() {
- var editor = this.editor;
- var data = editor.data;
- var editing = editor.editing;
+ _this.urlInput = new LabeledInput(urlInputModel, view.urlInputView, InputText, new Model());
- // Allow link attribute on all inline nodes.
- editor.document.schema.allow({ name: '$inline', attributes: 'linkHref' });
+ /**
+ * The save button inside {@link link.ui.LinkForm}.
+ *
+ * @member {ui.button.Button} link.ui.LinkForm#saveButton
+ */
+ _this.saveButton = new Button(new Model({
+ isEnabled: true,
+ isOn: false,
+ label: t('Save'),
+ withText: true,
+ type: 'submit'
+ }), view.saveButtonView);
- // Build converter from model to view for data and editing pipelines.
- buildModelConverter().for(data.modelToView, editing.modelToView).fromAttribute('linkHref').toElement(function (linkHref) {
- return new LinkElement('a', { href: linkHref });
- });
+ /**
+ * The cancel button inside {@link link.ui.LinkForm}.
+ *
+ * @member {ui.button.Button} link.ui.LinkForm#cancelButton
+ */
+ _this.cancelButton = new Button(new Model({
+ isEnabled: true,
+ isOn: false,
+ label: t('Cancel'),
+ withText: true
+ }), view.cancelButtonView);
- // Build converter from view to model for data pipeline.
- buildViewConverter().for(data.viewToModel).fromElement('a').toAttribute(function (viewElement) {
- return {
- key: 'linkHref',
- value: viewElement.getAttribute('href')
- };
- });
+ /**
+ * The unlink button inside {@link link.ui.LinkForm}.
+ *
+ * @member {ui.button.Button} link.ui.LinkForm#unlinkButton
+ */
+ _this.unlinkButton = new Button(new Model({
+ isEnabled: true,
+ isOn: false,
+ label: t('Unlink'),
+ icon: 'unlink'
+ }), view.unlinkButtonView);
+
+ view.delegate('submit').to(model);
+
+ // TODO: Delegate event with changed name.
+ _this.listenTo(_this.cancelButton.model, 'execute', function () {
+ _this.model.fire('cancel');
+ });
+
+ _this.listenTo(_this.unlinkButton.model, 'execute', function () {
+ _this.model.fire('unlink');
+ });
+
+ // TODO: add() should accept multiple items.
+ _this.add(_this.urlInput);
+ _this.add(_this.saveButton);
+ _this.add(_this.cancelButton);
+ _this.add(_this.unlinkButton);
+ return _this;
+ }
- // Create linking commands.
- editor.commands.set('link', new LinkCommand(editor));
- editor.commands.set('unlink', new UnlinkCommand(editor));
- }
- }]);
- return LinkEngine;
-}(Feature);
+ return LinkForm;
+}(Controller);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
-/* global document */
-
/**
- * Handles click outside of specified element and fires action.
+ * The label view class.
*
- * Note that it is not handled by a `click` event, this is to avoid situation when click on some trigger
- * opens and closes element at the same time.
+ * See {@link ui.label.Label}.
*
- * @param {Object} [options] Configuration options.
- * @param {ui.Controller} [options.controller] The controller to which this behavior should be added.
- * @param {utils.Observable} [options.model] Used together with `options.activeIf` to know when to listen for clicks.
- * @param {String} [options.activeIf] Used together with `options.model` to know when to listen for clicks.
- * @param {HTMLElement} [options.contextElement] Target element, click on it will not fire callback.
- * @param {Function} [options.callback] Function fired after clicking outside of specified element.
+ * @memberOf ui.label
+ * @extends ui.View
*/
-function clickOutsideHandler(options) {
- var controller = options.controller;
- var clickHandler = function clickHandler(evt, domEvt) {
- return handleClickOutside(domEvt.target, options.contextElement, options.callback);
- };
- controller.listenTo(options.model, 'change:' + options.activeIf, function (evt, name, value) {
- if (value) {
- controller.listenTo(document, 'mouseup', clickHandler);
- } else {
- controller.stopListening(document, 'mouseup', clickHandler);
- }
- });
+var LabelView = function (_View) {
+ inherits(LabelView, _View);
- // When `activeIf` property is `true` on init.
- if (options.model[options.activeIf]) {
- controller.listenTo(document, 'mouseup', clickHandler);
- }
-}
+ /**
+ * @inheritDoc
+ */
+ function LabelView(locale) {
+ classCallCheck(this, LabelView);
-// Fires callback when clicked element is outside of context element.
-//
-// @private
-// @param {HTMLElement} clickedElement Clicked element.
-// @param {HTMLElement} contextElement Click on this element will not fire callback.
-// @param {Function} callback Action fired after clicking outside of context element.
-function handleClickOutside(clickedElement, contextElement, callback) {
- if (!contextElement.contains(clickedElement)) {
- callback();
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LabelView).call(this, locale));
+
+ var bind = _this.bindTemplate;
+
+ _this.template = new Template({
+ tag: 'label',
+ attributes: {
+ class: ['ck-label'],
+ for: bind.to('for')
+ },
+ children: [{
+ text: bind.to('text')
+ }]
+ });
+
+ /**
+ * The text of the label.
+ *
+ * @observable
+ * @member {String} ui.label.LabelView#text
+ */
+
+ /**
+ * The `for` attribute of the label (i.e. to pair with an ` ` element).
+ *
+ * @observable
+ * @member {String} ui.label.LabelView#for
+ */
+ return _this;
}
-}
+
+ return LabelView;
+}(View);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
-/* global document */
-
/**
- * Handles Esc keydown and fires action.
+ * The labeled input view class.
*
- * @param {Object} [options] Configuration options.
- * @param {ui.Controller} [options.controller] The controller to which this behavior should be added.
- * @param {utils.Observable} [options.model] Used together with `options.activeIf` to know when to listen for keydown.
- * @param {String} [options.activeIf] Used together with `options.model` to know when to listen for keydown.
- * @param {Function} [options.callback] Function fired after Esc is pressed.
- * @returns {Function} Click handler
+ * See {@link ui.input.labeled.LabeledInput}.
+ *
+ * @memberOf ui.input.labeled
+ * @extends ui.View
*/
-function escPressHandler(options) {
- var controller = options.controller;
- var keypressHandler = function keypressHandler(evt, domEvt) {
- return handleEscPress(domEvt.keyCode, options.callback);
- };
- controller.listenTo(options.model, 'change:' + options.activeIf, function (evt, name, value) {
- if (value) {
- controller.listenTo(document, 'keydown', keypressHandler);
- } else {
- controller.stopListening(document, 'keydown', keypressHandler);
- }
- });
+var LabeledInputView = function (_View) {
+ inherits(LabeledInputView, _View);
- // When `activeIf` property is `true` on init.
- if (options.model[options.activeIf]) {
- controller.listenTo(document, 'keydown', keypressHandler);
- }
-}
+ /**
+ * Creates an instance of the labeled input view class.
+ *
+ * @param {Function} InputViewClass Constructor of the input view.
+ * @param {utils.Locale} [locale] The {@link core.editor.Editor#locale editor's locale} instance.
+ */
+ function LabeledInputView(InputViewClass, locale) {
+ classCallCheck(this, LabeledInputView);
-// Fires callback when ESC key was pressed.
-//
-// @private
-// @param {HTMLElement} keyCode Code of pressed key.
-// @param {Function} callback Action fired after ESC press.
-function handleEscPress(keyCode, callback) {
- if (keyCode == keyCodes.esc) {
- callback();
+ /**
+ * The label view.
+ *
+ * @member {ui.label.LabelView} ui.input.labeled.LabeledInput#labelView
+ */
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LabeledInputView).call(this, locale));
+
+ _this.labelView = new LabelView(_this.locale);
+
+ /**
+ * The input view.
+ *
+ * @member {ui.View} ui.input.labeled.LabeledInput#inputView
+ */
+ _this.inputView = new InputViewClass(_this.locale);
+
+ _this.template = new Template({
+ tag: 'div',
+
+ children: [_this.labelView, _this.inputView]
+ });
+ return _this;
}
-}
+
+ /**
+ * Moves the focus to the input and selects the value.
+ */
+
+
+ createClass(LabeledInputView, [{
+ key: 'select',
+ value: function select() {
+ this.inputView.select();
+ }
+ }]);
+ return LabeledInputView;
+}(View);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -48427,486 +47600,576 @@ function handleEscPress(keyCode, callback) {
*/
/**
- * The balloon panel controller class.
- *
- * new BalloonPanel( new Model(), new BalloonPanelView() );
+ * The text input view class.
*
- * See {@link ui.balloonPanel.BalloonPanelView}.
+ * See {@link ui.input.InputText}.
*
- * @memberOf ui.balloonPanel
- * @extends ui.Controller
+ * @memberOf ui.input
+ * @extends ui.View
*/
-var BalloonPanel = function (_Controller) {
- inherits(BalloonPanel, _Controller);
+var InputTextView = function (_View) {
+ inherits(InputTextView, _View);
- /**
- * Creates an instance of {@link ui.balloonPanel.BalloonPanel} class.
+ /**
+ * @inheritDoc
+ */
+ function InputTextView(locale) {
+ classCallCheck(this, InputTextView);
+
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(InputTextView).call(this, locale));
+
+ var bind = _this.bindTemplate;
+
+ _this.template = new Template({
+ tag: 'input',
+ attributes: {
+ type: 'text',
+ class: ['ck-input', 'ck-input-text'],
+ id: bind.to('id')
+ }
+ });
+
+ // Note: `value` cannot be an HTML attribute, because it doesn't change HTMLInputElement value once changed.
+ _this.on('change:value', function (evt, propertyName, value) {
+ return _this.element.value = value || '';
+ });
+
+ /**
+ * The value of the input.
*
- * @param {ui.balloonPanel.BalloonPanelModel} model Model of this balloon panel.
- * @param {ui.View} view View of this balloon panel.
+ * @observable
+ * @member {String} ui.input.InputTextView#value
*/
- function BalloonPanel(model, view) {
- classCallCheck(this, BalloonPanel);
-
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(BalloonPanel).call(this, model, view));
- view.set({
- top: 0,
- left: 0,
- arrow: 'se',
- isVisible: false
- });
+ /**
+ * The `id` attribute of the input (i.e. to pair with a `` element).
+ *
+ * @observable
+ * @member {String} ui.input.InputTextView#id
+ */
+ return _this;
+ }
- view.bind('maxWidth').to(model);
+ /**
+ * Moves the focus to the input and selects the value.
+ */
- _this.addCollection('content');
- return _this;
- }
- return BalloonPanel;
-}(Controller);
+ createClass(InputTextView, [{
+ key: 'select',
+ value: function select() {
+ this.element.select();
+ }
+ }]);
+ return InputTextView;
+}(View);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
-/* globals window, document, Range, HTMLElement */
+/**
+ * Handles native DOM `submit` event by preventing it and firing
+ * the {ui.View} `submit` event, which can be then handled by the
+ * parent controller.
+ *
+ * @param {Object} [options] Configuration options.
+ * @param {ui.View} options.view The view to which this behavior should be added.
+ */
+function submitHandler(_ref) {
+ var view = _ref.view;
-var toPx$1 = toUnit('px');
-var arrowLeftOffset = 30;
-var arrowTopOffset = 15;
+ view.listenTo(view.element, 'submit', function (evt, domEvt) {
+ domEvt.preventDefault();
+ view.fire('submit');
+ }, { useCapture: true });
+}
/**
- * The balloon panel view class.
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * The link form view controller class.
*
- * See {@link ui.balloonPanel.BalloonPanel}.
+ * See {@link link.ui.LinkForm}.
*
- * @memberOf ui.balloonPanel
+ * @memberOf link.ui
* @extends ui.View
*/
-var BalloonPanelView = function (_View) {
- inherits(BalloonPanelView, _View);
+var LinkFormView = function (_View) {
+ inherits(LinkFormView, _View);
/**
* @inheritDoc
*/
- function BalloonPanelView(locale) {
- classCallCheck(this, BalloonPanelView);
-
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(BalloonPanelView).call(this, locale));
-
- var bind = _this.bindTemplate;
-
- _this.template = new Template({
- tag: 'div',
- attributes: {
- class: ['ck-balloon-panel', bind.to('arrow', function (value) {
- return 'ck-balloon-panel_arrow_' + value;
- }), bind.if('isVisible', 'ck-balloon-panel_visible')],
-
- style: {
- top: bind.to('top', toPx$1),
- left: bind.to('left', toPx$1),
- maxWidth: bind.to('maxWidth', toPx$1)
- },
-
- // Make this element `focusable` to be available for adding to FocusTracker.
- tabindex: -1
- }
- });
-
- _this.register('content', function (el) {
- return el;
- });
+ function LinkFormView(locale) {
+ classCallCheck(this, LinkFormView);
/**
- * The absolute top position of the balloon panel in pixels.
+ * The url input view.
*
- * @observable
- * @member {Number} ui.balloonPanel.BalloonPanelView#top
+ * @member {ui.input.labeled.LabeledInputView} link.ui.LinkFormView#urlInputView
*/
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LinkFormView).call(this, locale));
- /**
- * The absolute left position of the balloon panel in pixels.
- *
- * @observable
- * @member {Number} ui.balloonPanel.BalloonPanelView#left
- */
+ _this.urlInputView = new LabeledInputView(InputTextView, locale);
/**
- * The maximum width of the balloon panel, as in CSS.
+ * The save button view.
*
- * @observable
- * @member {Number} ui.balloonPanel.BalloonPanelView#maxWidth
+ * @member {ui.button.ButtonView} link.ui.LinkFormView#saveButtonView
*/
+ _this.saveButtonView = new ButtonView(locale);
/**
- * Balloon panel arrow direction.
+ * The cancel button view.
*
- * @observable
- * @member {'se'|'sw'|'ne'|'nw'} ui.balloonPanel.BalloonPanelView#arrow
+ * @member {ui.button.ButtonView} link.ui.LinkFormView#cancelButtonView
*/
+ _this.cancelButtonView = new ButtonView(locale);
/**
- * Controls whether the balloon panel is visible or not.
+ * The unlink button view.
*
- * @observable
- * @member {Boolean} ui.balloonPanel.BalloonPanelView#isVisible
+ * @member {ui.button.ButtonView} link.ui.LinkFormView#unlinkButtonView
*/
+ _this.unlinkButtonView = new ButtonView(locale);
+
+ Template.extend(_this.saveButtonView.template, {
+ attributes: {
+ class: ['ck-button-action']
+ }
+ });
+
+ _this.template = new Template({
+ tag: 'form',
+
+ attributes: {
+ class: ['ck-link-form']
+ },
+
+ children: [_this.urlInputView, {
+ tag: 'div',
+
+ attributes: {
+ class: ['ck-link-form__actions']
+ },
+
+ children: [_this.saveButtonView, _this.cancelButtonView, _this.unlinkButtonView]
+ }]
+ });
+
+ submitHandler({
+ view: _this
+ });
return _this;
}
- /**
- * Shows the balloon panel.
- *
- * See {@link ui.balloonPanel.BalloonPanelView#isVisible}.
- */
+ return LinkFormView;
+}(View);
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * The link feature. It introduces the Link and Unlink buttons and the Ctrl+K keystroke.
+ *
+ * It uses the {@link link.LinkEngine link engine feature}.
+ *
+ * @memberOf link
+ * @extends core.Feature
+ */
+
+var Link = function (_Feature) {
+ inherits(Link, _Feature);
+
+ function Link() {
+ classCallCheck(this, Link);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Link).apply(this, arguments));
+ }
+
+ createClass(Link, [{
+ key: 'init',
- createClass(BalloonPanelView, [{
- key: 'show',
- value: function show() {
- this.isVisible = true;
- }
/**
- * Hides the balloon panel.
- *
- * See {@link ui.balloonPanel.BalloonPanelView#isVisible}.
+ * @inheritDoc
*/
+ value: function init() {
+ this.editor.editing.view.addObserver(ClickObserver);
- }, {
- key: 'hide',
- value: function hide() {
- this.isVisible = false;
+ /**
+ * Balloon panel component to display the main UI.
+ *
+ * @member {link.ui.LinkBalloonPanel} link.Link#balloonPanel
+ */
+ this.balloonPanel = this._createBalloonPanel();
+
+ /**
+ * The form component inside {@link link.Link#balloonPanel}.
+ *
+ * @member {link.ui.LinkForm} link.Link#form
+ */
+ this.form = this._createForm();
+
+ // Create toolbar buttons.
+ this._createToolbarLinkButton();
+ this._createToolbarUnlinkButton();
}
/**
- * Attaches the balloon panel to a specified DOM element or range with a smart heuristics.
- *
- * **Notes**:
- *
- * * The algorithm takes the geometry of the "limiter element" into consideration so,
- * if possible, the balloon is positioned within the rect of that element.
- * * If possible, the balloon is positioned within the area of the "limiter element"
- * fitting into the browser viewport visible to the user. It prevents the panel from
- * appearing off screen.
- *
- * The heuristics chooses from among 4 available positions relative to the target DOM element or range:
- *
- * * South east:
- *
- * [ Target ]
- * ^
- * +-----------------+
- * | |
- * +-----------------+
- *
- *
- * * South west:
- *
- * [ Target ]
- * ^
- * +-----------------+
- * | |
- * +-----------------+
- *
- *
- * * North east:
- *
- * +-----------------+
- * | |
- * +-----------------+
- * V
- * [ Target ]
- *
- *
- * * North west:
- *
- * +-----------------+
- * | |
- * +-----------------+
- * V
- * [ Target ]
+ * Creates a toolbar link button. Clicking this button will show
+ * {@link link.Link#balloonPanel} attached to the selection.
*
- * See {@ link ui.balloonPanel.BalloonPanelView#arrow}.
+ * @private
+ */
+
+ }, {
+ key: '_createToolbarLinkButton',
+ value: function _createToolbarLinkButton() {
+ var _this2 = this;
+
+ var editor = this.editor;
+ var linkCommand = editor.commands.get('link');
+ var t = editor.t;
+
+ // Create button model.
+ var linkButtonModel = new Model({
+ isEnabled: true,
+ isOn: false,
+ label: t('Link'),
+ icon: 'link',
+ keystroke: 'CTRL+K'
+ });
+
+ // Bind button model to the command.
+ linkButtonModel.bind('isEnabled').to(linkCommand, 'isEnabled');
+
+ // Show the panel on button click only when editor is focused.
+ this.listenTo(linkButtonModel, 'execute', function () {
+ return _this2._showPanel();
+ });
+
+ // Add link button to feature components.
+ editor.ui.featureComponents.add('link', Button, ButtonView, linkButtonModel);
+
+ // Handle `Ctrl+K` keystroke and show panel.
+ editor.keystrokes.set('CTRL+K', function () {
+ return _this2._showPanel();
+ });
+ }
+
+ /**
+ * Creates a toolbar unlink button. Clicking this button will unlink
+ * the selected link.
*
- * @param {HTMLElement|Range} elementOrRange Target DOM element or range to which the balloon will be attached.
- * @param {HTMLElement|Object} limiterElementOrRect The DOM element or element rect beyond which area the balloon panel should not be
- * positioned, if possible.
+ * @private
*/
}, {
- key: 'attachTo',
- value: function attachTo(elementOrRange, limiterElementOrRect) {
- this.show();
-
- var elementOrRangeRect = new AbsoluteDomRect(elementOrRange);
- var panelRect = new AbsoluteDomRect(this.element);
- var limiterVisibleRect = getAbsoluteRectVisibleInTheViewport(limiterElementOrRect);
-
- // Create a rect for each of the possible balloon positions and feed them to _smartAttachTo,
- // which will use whichever is the optimal. Position are ordered from most to less desired.
- var possiblePanelRects = {
- // The absolute rect for "South east" position.
- se: panelRect.clone().moveTo({
- top: elementOrRangeRect.bottom + arrowTopOffset,
- left: elementOrRangeRect.left + elementOrRangeRect.width / 2 - arrowLeftOffset
- }),
+ key: '_createToolbarUnlinkButton',
+ value: function _createToolbarUnlinkButton() {
+ var editor = this.editor;
+ var t = editor.t;
+ var unlinkCommand = editor.commands.get('unlink');
- // The absolute rect for "South west" position.
- sw: panelRect.clone().moveTo({
- top: elementOrRangeRect.bottom + arrowTopOffset,
- left: elementOrRangeRect.left + elementOrRangeRect.width / 2 - panelRect.width + arrowLeftOffset
- }),
+ // Create the button model.
+ var unlinkButtonModel = new Model({
+ isEnabled: false,
+ isOn: false,
+ label: t('Unlink'),
+ icon: 'unlink'
+ });
- // The absolute rect for "North east" position.
- ne: panelRect.clone().moveTo({
- top: elementOrRangeRect.top - panelRect.height - arrowTopOffset,
- left: elementOrRangeRect.left + elementOrRangeRect.width / 2 - arrowLeftOffset
- }),
+ // Bind button model to the command.
+ unlinkButtonModel.bind('isEnabled').to(unlinkCommand, 'isEnabled');
- // The absolute rect for "North west" position.
- nw: panelRect.clone().moveTo({
- top: elementOrRangeRect.top - panelRect.height - arrowTopOffset,
- left: elementOrRangeRect.left + elementOrRangeRect.width / 2 - panelRect.width + arrowLeftOffset
- })
- };
+ // Execute unlink command and hide panel, if open.
+ this.listenTo(unlinkButtonModel, 'execute', function () {
+ editor.execute('unlink');
+ });
- this._smartAttachTo(possiblePanelRects, limiterVisibleRect, panelRect.width * panelRect.height);
+ // Add unlink button to feature components.
+ editor.ui.featureComponents.add('unlink', Button, ButtonView, unlinkButtonModel);
}
/**
- * For the given set of possible rects, chooses the one which fits the best into both - browser viewport and
- * `visibleContainerRect`, which is when their intersection has the biggest area. Note that priority is a possible
- * highest intersection area with browser viewport.
+ * Creates the {@link link.ui.LinkBalloonPanel} instance.
*
* @private
- * @param {Object} rects Set of positions where balloon can be placed.
- * @param {AbsoluteDomRect} visibleContainerRect The absolute rect of the visible part of container element.
- * @param {Number} panelSurfaceArea Panel surface area.
+ * @returns {link.ui.LinkBalloonPanel} Link balloon panel instance.
*/
}, {
- key: '_smartAttachTo',
- value: function _smartAttachTo(rects, visibleContainerRect, panelSurfaceArea) {
- var viewportRect = new AbsoluteDomRect(getAbsoluteViewportRect());
- var maxIntersectRectPos = void 0;
- var maxContainerIntersectArea = -1;
- var maxViewportIntersectArea = -1;
+ key: '_createBalloonPanel',
+ value: function _createBalloonPanel() {
+ var _this3 = this;
- // Find the best place. Stop searching when the position with fully visible panel has been found.
- Object.keys(rects).some(function (rectPos) {
- var containerIntersectArea = rects[rectPos].getIntersectArea(visibleContainerRect);
- var viewportIntersectArea = rects[rectPos].getIntersectArea(viewportRect);
+ var editor = this.editor;
+ var viewDocument = editor.editing.view;
- if (viewportIntersectArea >= maxViewportIntersectArea && containerIntersectArea > maxContainerIntersectArea) {
- maxIntersectRectPos = rectPos;
- maxContainerIntersectArea = containerIntersectArea;
- maxViewportIntersectArea = viewportIntersectArea;
- }
+ // Create the balloon panel instance.
+ var balloonPanel = new BalloonPanel(new Model({
+ maxWidth: 300
+ }), new BalloonPanelView(editor.locale));
- return maxContainerIntersectArea === panelSurfaceArea;
- });
+ // Add balloonPanel.view#element to FocusTracker.
+ // @TODO: Do it automatically ckeditor5-core#23
+ editor.focusTracker.add(balloonPanel.view.element);
- // Move the balloon panel.
- this.arrow = maxIntersectRectPos;
- this.top = rects[maxIntersectRectPos].top;
- this.left = rects[maxIntersectRectPos].left;
- }
- }]);
- return BalloonPanelView;
-}(View);
+ // Handle click on view document and show panel when selection is placed inside the link element.
+ // Keep panel open until selection will be inside the same link element.
+ this.listenTo(viewDocument, 'click', function () {
+ var viewSelection = viewDocument.selection;
+ var parentLink = getPositionParentLink(viewSelection.getFirstPosition());
-var AbsoluteDomRect = function () {
- // Create instance of AbsoluteDomRect class.
- //
- // @param {HTMLElement|Range|Object} elementOrRangeOrRect Source object to create the rect.
- function AbsoluteDomRect(elementOrRangeOrRect) {
- classCallCheck(this, AbsoluteDomRect);
+ if (viewSelection.isCollapsed && parentLink) {
+ _this3._attachPanelToElement();
- Object.assign(this, getAbsoluteRect(elementOrRangeOrRect));
- }
+ _this3.listenTo(viewDocument, 'render', function () {
+ var currentParentLink = getPositionParentLink(viewSelection.getFirstPosition());
- // Clone instance of this class.
- //
- // @returns {AbsoluteDomRect}
+ if (!viewSelection.isCollapsed || parentLink !== currentParentLink) {
+ _this3._hidePanel();
+ } else {
+ _this3._attachPanelToElement(parentLink);
+ }
+ });
+ _this3.listenTo(balloonPanel.view, 'change:isVisible', function () {
+ return _this3.stopListening(viewDocument, 'render');
+ });
+ }
+ });
- createClass(AbsoluteDomRect, [{
- key: 'clone',
- value: function clone() {
- return new AbsoluteDomRect(this);
+ // Close on `ESC` press.
+ escPressHandler({
+ controller: balloonPanel.view,
+ model: balloonPanel.view,
+ activeIf: 'isVisible',
+ callback: function callback() {
+ return _this3._hidePanel(true);
+ }
+ });
+
+ // Close on click outside of balloon panel element.
+ clickOutsideHandler({
+ controller: balloonPanel.view,
+ model: balloonPanel.view,
+ activeIf: 'isVisible',
+ contextElement: balloonPanel.view.element,
+ callback: function callback() {
+ return _this3._hidePanel();
+ }
+ });
+
+ editor.ui.add('body', balloonPanel);
+
+ return balloonPanel;
}
- // Move current box to specified position.
- //
- // @param {Number} top New to position.
- // @param {Number} left New left position.
- // @returns {AbsoluteDomRect}
+ /**
+ * Creates the {@link link.ui.LinkForm} instance.
+ *
+ * @private
+ * @returns {link.ui.LinkForm} Link form instance.
+ */
}, {
- key: 'moveTo',
- value: function moveTo(_ref) {
- var top = _ref.top;
- var left = _ref.left;
+ key: '_createForm',
+ value: function _createForm() {
+ var _this4 = this;
- this.top = top;
- this.right = left + this.width;
- this.bottom = top + this.height;
- this.left = left;
+ var editor = this.editor;
+ var formModel = new Model();
- return this;
- }
+ formModel.bind('url').to(editor.commands.get('link'), 'value');
- // Get intersect surface area of this AbsoluteDomRect and other AbsoluteDomRect.
- //
- // @param {AbsoluteDomRect} rect
- // @returns {Number} Overlap surface area.
+ var form = new LinkForm(formModel, new LinkFormView(editor.locale));
- }, {
- key: 'getIntersectArea',
- value: function getIntersectArea(rect) {
- var hOverlap = Math.max(0, Math.min(this.right, rect.right) - Math.max(this.left, rect.left));
- var vOverlap = Math.max(0, Math.min(this.bottom, rect.bottom) - Math.max(this.top, rect.top));
+ // Execute link command after clicking on balloon panel `Link` button.
+ this.listenTo(formModel, 'submit', function () {
+ editor.execute('link', _this4.form.urlInput.value);
+ _this4._hidePanel(true);
+ });
- return hOverlap * vOverlap;
- }
- }]);
- return AbsoluteDomRect;
-}();
+ // Execute unlink command after clicking on balloon panel `Unlink` button.
+ this.listenTo(formModel, 'unlink', function () {
+ editor.execute('unlink');
+ _this4._hidePanel(true);
+ });
-// Returns the client rect of an HTMLElement, Range, or rect. The obtained geometry of the rect
-// corresponds with `position: absolute` relative to the `` (`document.body`).
-//
-// @private
-// @param {HTMLElement|Range|Object} elementOrRangeOrRect Target object witch rect is to be determined.
-// @returns {Object} Client rect object.
+ // Hide balloon panel after clicking on balloon panel `Cancel` button.
+ this.listenTo(formModel, 'cancel', function () {
+ return _this4._hidePanel(true);
+ });
+
+ this.balloonPanel.add('content', form);
+
+ return form;
+ }
+ /**
+ * Shows {@link link.Link#balloonPanel LinkBalloonPanel} and attach to target element.
+ * If selection is collapsed and is placed inside link element, then panel will be attached
+ * to whole link element, otherwise will be attached to the selection.
+ *
+ * @private
+ * @param {link.LinkElement} [parentLink] Target element.
+ */
-function getAbsoluteRect(elementOrRangeOrRect) {
- var bodyRect = document.body.getBoundingClientRect();
+ }, {
+ key: '_attachPanelToElement',
+ value: function _attachPanelToElement(parentLink) {
+ var viewDocument = this.editor.editing.view;
+ var domEditableElement = viewDocument.domConverter.getCorrespondingDomElement(viewDocument.selection.editableElement);
+ var targetLink = parentLink || getPositionParentLink(viewDocument.selection.getFirstPosition());
- if (elementOrRangeOrRect instanceof HTMLElement || elementOrRangeOrRect instanceof Range) {
- var elementRect = elementOrRangeOrRect.getBoundingClientRect();
+ // When selection is inside link element, then attach panel to this element.
+ if (targetLink) {
+ this.balloonPanel.view.attachTo(viewDocument.domConverter.getCorrespondingDomElement(targetLink), domEditableElement);
+ }
+ // Otherwise attach panel to the selection.
+ else {
+ this.balloonPanel.view.attachTo(viewDocument.domConverter.viewRangeToDom(viewDocument.selection.getFirstRange()), domEditableElement);
+ }
+ }
- return {
- top: elementRect.top - bodyRect.top,
- right: elementRect.right - bodyRect.left,
- bottom: elementRect.bottom - bodyRect.top,
- left: elementRect.left - bodyRect.left,
- width: elementRect.width,
- height: elementRect.height
- };
- }
+ /**
+ * Hides {@link link.Link#balloonPanel LinkBalloonPanel}.
+ *
+ * @private
+ * @param {Boolean} [focusEditable=false] When `true` then editable focus will be restored on panel hide.
+ */
- // The rect has been passed.
- var absoluteRect = Object.assign({}, elementOrRangeOrRect);
+ }, {
+ key: '_hidePanel',
+ value: function _hidePanel(focusEditable) {
+ this.balloonPanel.view.hide();
- if (absoluteRect.width === undefined) {
- absoluteRect.width = absoluteRect.right - absoluteRect.left;
- }
+ if (focusEditable) {
+ this.editor.editing.view.focus();
+ }
+ }
- if (absoluteRect.height === undefined) {
- absoluteRect.height = absoluteRect.bottom - absoluteRect.top;
- }
+ /**
+ * Shows {@link link.Link#balloonPanel LinkBalloonPanel}.
+ *
+ * @private
+ */
- return absoluteRect;
-}
+ }, {
+ key: '_showPanel',
+ value: function _showPanel() {
+ this._attachPanelToElement();
+ this.form.urlInput.view.select();
+ }
+ }], [{
+ key: 'requires',
-// Returns the client rect of the element limited by the visible (to the user)
-// viewport of the browser window.
-//
-// [Browser viewport]
-// +---------------------------------------+
-// | [Element] |
-// | +----------------------+
-// | |##############| |
-// | |##############| |
-// | |#######^######| |
-// | +-------|--------------+
-// | | |
-// +--------------------------------|------+
-// |
-// \- [Element rect visible in the viewport]
-//
-// @private
-// @param {HTMLElement|Object} element Object which visible area rect is to be determined.
-// @returns {AbsoluteDomRect} An absolute rect of the area visible in the viewport.
-function getAbsoluteRectVisibleInTheViewport(element) {
- var elementRect = getAbsoluteRect(element);
- var viewportRect = getAbsoluteViewportRect();
+ /**
+ * @inheritDoc
+ */
+ get: function get() {
+ return [LinkEngine];
+ }
+ }]);
+ return Link;
+}(Feature);
- return new AbsoluteDomRect({
- top: Math.max(elementRect.top, viewportRect.top),
- left: Math.max(elementRect.left, viewportRect.left),
- right: Math.min(elementRect.right, viewportRect.right),
- bottom: Math.min(elementRect.bottom, viewportRect.bottom)
+function getPositionParentLink(position) {
+ return position.parent.getAncestors().find(function (ancestor) {
+ return ancestor instanceof LinkElement;
});
}
-// Get browser viewport rect.
-//
-// @private
-// @returns {Object} Viewport rect.
-function getAbsoluteViewportRect() {
- var windowScrollX = window.scrollX;
- var windowScrollY = window.scrollY;
- var windowWidth = window.innerWidth;
- var windowHeight = window.innerHeight;
-
- return {
- top: windowScrollY,
- right: windowWidth + windowScrollX,
- bottom: windowHeight + windowScrollY,
- left: windowScrollX
- };
-}
-
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/
/**
- * The label controller class.
- *
- * const model = new Model( {
- * text: 'Label of some input.',
- * for: 'id-of-related-input'
- * } );
+ * Utilities used in modules from {@link list list} package.
*
- * new Label( model, new LabelView() );
+ * @memberOf list
+ * @namespace list.utils
+ */
+
+/**
+ * For given {@link engine.model.Position position}, returns the closest ancestor of that position which is a
+ * `listItem` element.
*
- * See {@link ui.input.LabelView}.
+ * @function list.utils.getClosestListItem
+ * @param {engine.model.Position} position Position which ancestor should be check looking for `listItem` element.
+ * @returns {engine.model.Element|null} Element with `listItem` name that is a closest ancestor of given `position`, or
+ * `null` if neither of `position` ancestors is a `listItem`.
+ */
+function getClosestListItem(position) {
+ return Array.from(position.getAncestors()).find(function (parent) {
+ return parent.name == 'listItem';
+ }) || null;
+}
+
+/**
+ * For given {@link engine.model.Selection selection} and {@link engine.model.Schema schema}, returns an array with
+ * all elements that are in the selection and are extending `$block` schema item.
*
- * @memberOf ui.label
- * @extends ui.Controller
+ * @function list.utils.getSelectedBlocks
+ * @param {engine.model.Selection} selection Selection from which blocks will be taken.
+ * @param {engine.model.Schema} schema Schema which will be used to check if a model element extends `$block`.
+ * @returns {Array.} All blocks from the selection.
*/
+function getSelectedBlocks(selection, schema) {
+ var position = getPositionBeforeBlock(selection.getFirstPosition(), schema);
-var Label = function (_Controller) {
- inherits(Label, _Controller);
+ var endPosition = selection.getLastPosition();
+ var blocks = [];
- /**
- * Creates an instance of {@link ui.label.Label} class.
- *
- * @param {ui.label.LabelModel} model Model of this label.
- * @param {ui.View} view View of this label.
- */
- function Label(model, view) {
- classCallCheck(this, Label);
+ // Traverse model from the first position before a block to the end position of selection.
+ // Store all elements that were after the correct positions.
+ while (position !== null && position.isBefore(endPosition)) {
+ blocks.push(position.nodeAfter);
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(Label).call(this, model, view));
+ position.offset++;
+ position = getPositionBeforeBlock(position, schema);
+ }
- view.bind('text', 'for').to(model);
- return _this;
+ return blocks;
+}
+
+/**
+ * For given {@link engine.model.Position position}, finds a model element extending `$block` schema item which is
+ * closest element to that position. First node after the position is checked and then the position's ancestors. `null`
+ * is returned if such element has not been found or found element is a root element.
+ *
+ * @param position
+ * @param schema
+ * @returns {*}
+ */
+function getPositionBeforeBlock(position, schema) {
+ // Start from the element right after the position. Maybe it is already a `$block` element.
+ var element = position.nodeAfter;
+
+ // If the position is not before an element, check the parent.
+ if (!element) {
+ element = position.parent;
}
- return Label;
-}(Controller);
+ // If proper element is still not found, check the ancestors.
+ while (element !== null && !schema.itemExtends(element.name || '$text', '$block')) {
+ element = element.parent;
+ }
+
+ // If proper element has been found, return position before it, otherwise return null;
+ return element !== null && element.parent !== null ? Position.createBefore(element) : null;
+}
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -48914,171 +48177,265 @@ var Label = function (_Controller) {
*/
/**
- * The labeled input controller class. It contains two components, {@link ui.input.Label Label}
- * and `InputComponent` instance, connected by an unique id.
- *
- * const input = new InputText( new Model(), new InputTextView() );
- *
- * const model = new Model( {
- * label: 'Label text'
- * value: 'init value',
- * } );
- *
- * new LabeledInput( model, new LabeledInputView(), input );
- *
- * See {@link ui.input.labeled.LabeledInputView}.
+ * The list command. It is used by the {@link list.List list feature}.
*
- * @memberOf ui.input.labeled
- * @extends ui.Controller
+ * @memberOf list
+ * @extends core.command.Command
*/
-var LabeledInput = function (_Controller) {
- inherits(LabeledInput, _Controller);
+var ListCommand = function (_Command) {
+ inherits(ListCommand, _Command);
/**
- * Creates an instance of {@link ui.input.labeled.LabeledInput} class.
+ * Creates an instance of the command.
*
- * @param {ui.input.labeled.LabeledInputModel} model Model of this input.
- * @param {ui.View} view View of this input.
- * @param {Function} InputClass Constructor of the input component.
- * @param {ui.Model} model Model of the input component.
+ * @param {core.editor.Editor} editor Editor instance.
+ * @param {'numbered'|'bulleted'} type List type that will be handled by this command.
*/
- function LabeledInput(model, view, InputClass, inputModel) {
- classCallCheck(this, LabeledInput);
-
- /**
- * An unique `id` to pair the input with its label.
- *
- * @protected
- * @member {ui.input.Label}
- */
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LabeledInput).call(this, model, view));
-
- _this._uid = 'ck-input-' + uid();
+ function ListCommand(editor, type) {
+ classCallCheck(this, ListCommand);
/**
- * The `Label` controller instance.
+ * The type of list created by the command.
*
- * @member {ui.input.Label}
+ * @readonly
+ * @member {'numbered'|'bulleted'} list.ListCommand#type
*/
- _this.label = _this._createLabel();
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(ListCommand).call(this, editor));
+
+ _this.type = type == 'bulleted' ? 'bulleted' : 'numbered';
/**
- * The input controller instance.
+ * Flag indicating whether the command is active, which means that selection starts in a list of the same type.
*
- * @member {ui.input.InputText}
+ * @observable
+ * @member {Boolean} list.ListCommand#value
*/
- _this.input = _this._spawnInput(InputClass, inputModel);
+ _this.set('value', false);
+
+ var changeCallback = function changeCallback() {
+ _this.refreshValue();
+ _this.refreshState();
+ };
+
+ // Listen on selection and document changes and set the current command's value.
+ _this.listenTo(editor.document.selection, 'change:range', changeCallback);
+ _this.listenTo(editor.document, 'changesDone', changeCallback);
return _this;
}
/**
- * Returns the input value.
- *
- * @returns {String} Input value.
+ * Sets command's value based on the document selection.
*/
- createClass(LabeledInput, [{
- key: '_createLabel',
+ createClass(ListCommand, [{
+ key: 'refreshValue',
+ value: function refreshValue() {
+ var position = this.editor.document.selection.getFirstPosition();
+ // Check whether closest `listItem` ancestor of the position has a correct type.
+ var listItem = getClosestListItem(position);
+ this.value = listItem !== null && listItem.getAttribute('type') == this.type;
+ }
/**
- * Initializes the {@link ui.input.Label Label} instance.
+ * Executes command.
*
- * @private
- * @returns {Label}
+ * @protected
+ * @param {Object} [options] Options for executed command.
+ * @param {engine.model.Batch} [options.batch] Batch to collect all the change steps.
+ * New batch will be created if this option is not set.
*/
- value: function _createLabel() {
- var model = new Model();
- model.bind('text').to(this.model, 'label');
- model.set('for', this._uid);
+ }, {
+ key: '_doExecute',
+ value: function _doExecute() {
+ var _this2 = this;
- return new Label(model, this.view.labelView);
- }
+ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
- /**
- * Creates the new input using given constructor and model.
- * Binds basic attributes of the newly created input's model to {ui.input.labeled.LabeledInputModel}.
- *
- * @private
- * @param {Function} InputClass A constructor of the input component.
- * @param {ui.Model} inputModel Model of the input component.
- * @returns {ui.Controller} An instance of the input component.
- */
+ var document = this.editor.document;
+ var blocks = getSelectedBlocks(document.selection, document.schema);
- }, {
- key: '_spawnInput',
- value: function _spawnInput(InputClass, inputModel) {
- inputModel.bind('value').to(this.model, 'value');
- inputModel.set('id', this._uid);
+ // Whether we are turning off some items.
+ var turnOff = this.value === true;
+ // If we are turning off items, we are going to rename them to paragraphs.
- return new InputClass(inputModel, this.view.inputView);
- }
- }, {
- key: 'value',
- get: function get() {
- return this.input.value;
- }
- }]);
- return LabeledInput;
-}(Controller);
+ document.enqueueChanges(function () {
+ var batch = options.batch || document.batch();
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ // If part of a list got turned off, we need to handle (outdent) all of sub-items of the last turned-off item.
+ // To be sure that model is all the time in a good state, we first fix items below turned-off item.
+ if (turnOff) {
+ // Start from the model item that is just after the last turned-off item.
+ var next = blocks[blocks.length - 1].nextSibling;
+ var currentIndent = Number.POSITIVE_INFINITY;
+ var changes = [];
-/**
- * The text input controller class.
- *
- * const model = new Model( {
- * value: 'Initial value.',
- * id: 'id-of-this-input'
- * } );
- *
- * new InputText( model, new InputTextView() );
- *
- * See {@link ui.input.InputTextView}.
- *
- * @memberOf ui.input
- * @extends ui.Controller
- */
+ // Correct indent of all items after the last turned off item.
+ // Rules that should be followed:
+ // 1. All direct sub-items of turned-off item should become indent 0, because the first item after it
+ // will be the first item of a new list. Other items are at the same level, so should have same 0 index.
+ // 2. All items with indent lower than indent of turned-off item should become indent 0, because they
+ // should not end up as a child of any of list items that they were not children of before.
+ // 3. All other items should have their indent changed relatively to it's parent.
+ //
+ // For example:
+ // 1 * --------
+ // 2 * --------
+ // 3 * -------- <- this is turned off.
+ // 4 * -------- <- this has to become indent = 0, because it will be first item on a new list.
+ // 5 * -------- <- this should be still be a child of item above, so indent = 1.
+ // 6 * -------- <- this also has to become indent = 0, because it shouldn't end up as a child of any of items above.
+ // 7 * -------- <- this should be still be a child of item above, so indent = 1.
+ // 8 * -------- <- this has to become indent = 0.
+ // 9 * -------- <- this should still be a child of item above, so indent = 1.
+ // 10 * -------- <- this should still be a child of item above, so indent = 2.
+ // 11 * -------- <- this should still be at the same level as item above, so indent = 2.
+ // 12 * -------- <- this and all below are left unchanged.
+ // 13 * --------
+ // 14 * --------
+ //
+ // After turning off 3 the list becomes:
+ //
+ // 1 * --------
+ // 2 * --------
+ //
+ // 3 --------
+ //
+ // 4 * --------
+ // 5 * --------
+ // 6 * --------
+ // 7 * --------
+ // 8 * --------
+ // 9 * --------
+ // 10 * --------
+ // 11 * --------
+ // 12 * --------
+ // 13 * --------
+ // 14 * --------
+ //
+ // Thanks to this algorithm no lists are mismatched and no items get unexpected children/parent, while
+ // those parent-child connection which are possible to maintain are still maintained. It's worth noting
+ // that this is the same effect that we would be get by multiple use of outdent command. However doing
+ // it like this is much more efficient because it's less operation (less memory usage, easier OT) and
+ // less conversion (faster).
+ while (next && next.name == 'listItem' && next.getAttribute('indent') !== 0) {
+ // Check each next list item, as long as its indent is bigger than 0.
+ // If the indent is 0 we are not going to change anything anyway.
+ var indent = next.getAttribute('indent');
-var InputText = function (_Controller) {
- inherits(InputText, _Controller);
+ // We check if that's item indent is lower as current relative indent.
+ if (indent < currentIndent) {
+ // If it is, current relative indent becomes that indent.
+ currentIndent = indent;
+ }
- /**
- * Creates an instance of {@link ui.input.InputText} class.
- *
- * @param {ui.input.InputTextModel} model Model of this input.
- * @param {ui.View} view View of this input.
- */
- function InputText(model, view) {
- classCallCheck(this, InputText);
+ // Fix indent relatively to current relative indent.
+ // Note, that if we just changed the current relative indent, the newIndent will be equal to 0.
+ var newIndent = indent - currentIndent;
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(InputText).call(this, model, view));
+ // Save the entry in changes array. We do not apply it at the moment, because we will need to
+ // reverse the changes so the last item is changed first.
+ // This is to keep model in correct state all the time.
+ changes.push({ element: next, indent: newIndent });
- view.bind('value', 'id').to(model);
- return _this;
- }
+ // Find next item.
+ next = next.nextSibling;
+ }
+
+ changes = changes.reverse();
+
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = changes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var item = _step.value;
+
+ batch.setAttribute(item.element, 'indent', item.indent);
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ }
+
+ // Phew! Now it will be easier :).
+ // For each block element that was in the selection, we will either: turn it to list item,
+ // turn it to paragraph, or change it's type. Or leave it as it is.
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+
+ try {
+ for (var _iterator2 = blocks[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var element = _step2.value;
+
+ if (turnOff && element.name == 'listItem') {
+ // We are turning off and the element is a `listItem` - it should be converted to `paragraph`.
+ // The order is important to keep model in correct state.
+ batch.rename(element, 'paragraph').removeAttribute(element, 'type').removeAttribute(element, 'indent');
+ } else if (!turnOff && element.name != 'listItem') {
+ // We are turning on and the element is not a `listItem` - it should be converted to `listItem`.
+ // The order is important to keep model in correct state.
+ batch.setAttribute(element, 'type', _this2.type).setAttribute(element, 'indent', 0).rename(element, 'listItem');
+ } else if (!turnOff && element.name == 'listItem' && element.getAttribute('type') != _this2.type) {
+ // We are turning on and the element is a `listItem` but has different type - change type.
+ batch.setAttribute(element, 'type', _this2.type);
+ }
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
+ }
+ });
+ }
- /**
- * Returns the input value.
- *
- * @returns {String} input value.
+ /**
+ * @inheritDoc
*/
+ }, {
+ key: '_checkEnabled',
+ value: function _checkEnabled() {
+ // If command is enabled it means that we are in list item, so the command should be enabled.
+ if (this.value) {
+ return true;
+ }
- createClass(InputText, [{
- key: 'value',
- get: function get() {
- return this.view.element.value;
- }
- }]);
- return InputText;
-}(Controller);
+ var selection = this.editor.document.selection;
+ var schema = this.editor.document.schema;
+ var position = getPositionBeforeBlock(selection.getFirstPosition(), schema);
+
+ // Otherwise, check if list item can be inserted at the position start.
+ return schema.check({ name: 'listItem', inside: position, attributes: ['type', 'indent'] });
+ }
+ }]);
+ return ListCommand;
+}(Command);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -49086,160 +48443,160 @@ var InputText = function (_Controller) {
*/
/**
- * The link form class.
- *
- * new LinkForm( new Model(), new LinkFormView() );
- *
- * See {@link link.ui.LinkFormView}.
+ * The list indent command. It is used by the {@link list.List list feature}.
*
- * @memberOf link.ui
- * @extends ui.Controller
+ * @memberOf list
+ * @extends core.command.Command
*/
-var LinkForm = function (_Controller) {
- inherits(LinkForm, _Controller);
+var IndentCommand = function (_Command) {
+ inherits(IndentCommand, _Command);
/**
- * Creates an instance of {@link link.ui.LinkForm} class.
+ * Creates an instance of the command.
*
- * @param {link.ui.LinkFormModel} model Model of this link form.
- * @param {ui.View} view View of this link form.
+ * @param {core.editor.Editor} editor Editor instance.
+ * @param {'forward'|'backward'} indentDirection Direction of indent. If it is equal to `backward`, the command
+ * will outdent a list item.
*/
- function LinkForm(model, view) {
- classCallCheck(this, LinkForm);
+ function IndentCommand(editor, indentDirection) {
+ classCallCheck(this, IndentCommand);
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LinkForm).call(this, model, view));
+ /**
+ * By how much the command will change list item's indent attribute.
+ *
+ * @readonly
+ * @private
+ * @member {Number} list.IndentCommand#_indentBy
+ */
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(IndentCommand).call(this, editor));
- var t = _this.view.t;
- var urlInputModel = new Model({
- label: t('Link URL')
+ _this._indentBy = indentDirection == 'forward' ? 1 : -1;
+
+ // Refresh command state after selection is changed or changes has been done to the document.
+ _this.listenTo(editor.document.selection, 'change:range', function () {
+ _this.refreshState();
});
- // Bind LabeledInputModel#value to LinkFormModel#url.
- urlInputModel.bind('value').to(model, 'url');
+ _this.listenTo(editor.document, 'changesDone', function () {
+ _this.refreshState();
+ });
+ return _this;
+ }
- /**
- * The URL input inside {@link link.ui.LinkForm}.
- *
- * @member {ui.input.labeled.LabeledInput} link.ui.LinkForm#urlInput
- */
- _this.urlInput = new LabeledInput(urlInputModel, view.urlInputView, InputText, new Model());
+ /**
+ * @inheritDoc
+ */
- /**
- * The save button inside {@link link.ui.LinkForm}.
- *
- * @member {ui.button.Button} link.ui.LinkForm#saveButton
- */
- _this.saveButton = new Button(new Model({
- isEnabled: true,
- isOn: false,
- label: t('Save'),
- withText: true,
- type: 'submit'
- }), view.saveButtonView);
- /**
- * The cancel button inside {@link link.ui.LinkForm}.
- *
- * @member {ui.button.Button} link.ui.LinkForm#cancelButton
- */
- _this.cancelButton = new Button(new Model({
- isEnabled: true,
- isOn: false,
- label: t('Cancel'),
- withText: true
- }), view.cancelButtonView);
+ createClass(IndentCommand, [{
+ key: '_doExecute',
+ value: function _doExecute() {
+ var _this2 = this;
- /**
- * The unlink button inside {@link link.ui.LinkForm}.
- *
- * @member {ui.button.Button} link.ui.LinkForm#unlinkButton
- */
- _this.unlinkButton = new Button(new Model({
- isEnabled: true,
- isOn: false,
- label: t('Unlink'),
- icon: 'unlink'
- }), view.unlinkButtonView);
+ var doc = this.editor.document;
+ var batch = doc.batch();
+ var element = getClosestListItem(doc.selection.getFirstPosition());
- view.delegate('submit').to(model);
+ doc.enqueueChanges(function () {
+ var oldIndent = element.getAttribute('indent');
- // TODO: Delegate event with changed name.
- _this.listenTo(_this.cancelButton.model, 'execute', function () {
- _this.model.fire('cancel');
- });
+ var itemsToChange = [element];
- _this.listenTo(_this.unlinkButton.model, 'execute', function () {
- _this.model.fire('unlink');
- });
+ // Indenting a list item should also indent all the items that are already sub-items of indented item.
+ var next = element.nextSibling;
- // TODO: add() should accept multiple items.
- _this.add(_this.urlInput);
- _this.add(_this.saveButton);
- _this.add(_this.cancelButton);
- _this.add(_this.unlinkButton);
- return _this;
- }
+ // Check all items as long as their indent is bigger than indent of changed list item.
+ while (next && next.name == 'listItem' && next.getAttribute('indent') > oldIndent) {
+ itemsToChange.push(next);
- return LinkForm;
-}(Controller);
+ next = next.nextSibling;
+ }
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ // We need to be sure to keep model in correct state after each small change, because converters
+ // bases on that state and assumes that model is correct.
+ // Because of that, if the command outdented items, we will outdent them starting from the last item, as
+ // it is safer.
+ if (_this2._indentBy < 0) {
+ itemsToChange = itemsToChange.reverse();
+ }
-/**
- * The label view class.
- *
- * See {@link ui.label.Label}.
- *
- * @memberOf ui.label
- * @extends ui.View
- */
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
-var LabelView = function (_View) {
- inherits(LabelView, _View);
+ try {
+ for (var _iterator = itemsToChange[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var item = _step.value;
- /**
- * @inheritDoc
- */
- function LabelView(locale) {
- classCallCheck(this, LabelView);
+ var indent = item.getAttribute('indent') + _this2._indentBy;
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LabelView).call(this, locale));
+ // If indent is lower than 0, it means that the item got outdented when it was not indented.
+ // This means that we need to convert that list item to paragraph.
+ if (indent < 0) {
+ // To keep the model as correct as possible, first rename listItem, then remove attributes,
+ // as listItem without attributes is very incorrect and will cause problems in converters.
+ batch.rename(item, 'paragraph').removeAttribute(item, 'indent').removeAttribute(item, 'type');
+ } else {
+ // If indent is >= 0, just change the attribute value.
+ batch.setAttribute(item, 'indent', indent);
+ }
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ });
+ }
- var bind = _this.bindTemplate;
+ /**
+ * @inheritDoc
+ */
- _this.template = new Template({
- tag: 'label',
- attributes: {
- class: ['ck-label'],
- for: bind.to('for')
- },
- children: [{
- text: bind.to('text')
- }]
- });
+ }, {
+ key: '_checkEnabled',
+ value: function _checkEnabled() {
+ // Check whether any of position's ancestor is a list item.
+ var listItem = getClosestListItem(this.editor.document.selection.getFirstPosition());
+
+ // If selection is not in a list item, the command is disabled.
+ if (!listItem) {
+ return false;
+ }
+
+ var prev = listItem.previousSibling;
+ var oldIndent = listItem.getAttribute('indent');
+ var newIndent = oldIndent + this._indentBy;
- /**
- * The text of the label.
- *
- * @observable
- * @member {String} ui.label.LabelView#text
- */
+ if (this._indentBy > 0) {
+ // If we are indenting, there are some conditions to meet.
+ // Cannot indent first list item.
+ if (!prev || prev.name != 'listItem') {
+ return false;
+ }
- /**
- * The `for` attribute of the label (i.e. to pair with an ` ` element).
- *
- * @observable
- * @member {String} ui.label.LabelView#for
- */
- return _this;
- }
+ // Indent can be at most greater by one than indent of previous item.
+ if (prev.getAttribute('indent') + 1 < newIndent) {
+ return false;
+ }
+ }
- return LabelView;
-}(View);
+ // If we are outdenting it is enough to be in list item. Every list item can always be outdented.
+ return true;
+ }
+ }]);
+ return IndentCommand;
+}(Command);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -49247,560 +48604,712 @@ var LabelView = function (_View) {
*/
/**
- * The labeled input view class.
- *
- * See {@link ui.input.labeled.LabeledInput}.
+ * View element class representing list item (``). It extends {@link engine.view.ContainerElement} and overwrites
+ * {@link list.ViewListItemElement#getFillerOffset evaluating whether filler offset} is needed.
*
- * @memberOf ui.input.labeled
- * @extends ui.View
+ * @memberOf list
+ * @extends engine.view.ContainerElement
*/
-var LabeledInputView = function (_View) {
- inherits(LabeledInputView, _View);
+var ViewListItemElement = function (_ViewContainerElement) {
+ inherits(ViewListItemElement, _ViewContainerElement);
/**
- * Creates an instance of the labeled input view class.
+ * Creates ` ` view item.
*
- * @param {Function} InputViewClass Constructor of the input view.
- * @param {utils.Locale} [locale] The {@link core.editor.Editor#locale editor's locale} instance.
+ * @param {Object|Iterable} [attrs] Collection of attributes.
+ * @param {engine.view.Node|Iterable.} [children] List of nodes to be inserted into created element.
*/
- function LabeledInputView(InputViewClass, locale) {
- classCallCheck(this, LabeledInputView);
-
- /**
- * The label view.
- *
- * @member {ui.label.LabelView} ui.input.labeled.LabeledInput#labelView
- */
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LabeledInputView).call(this, locale));
-
- _this.labelView = new LabelView(_this.locale);
-
- /**
- * The input view.
- *
- * @member {ui.View} ui.input.labeled.LabeledInput#inputView
- */
- _this.inputView = new InputViewClass(_this.locale);
-
- _this.template = new Template({
- tag: 'div',
-
- children: [_this.labelView, _this.inputView]
- });
- return _this;
+ function ViewListItemElement(attrs, children) {
+ classCallCheck(this, ViewListItemElement);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(ViewListItemElement).call(this, 'li', attrs, children));
}
/**
- * Moves the focus to the input and selects the value.
+ * @inheritDoc
*/
- createClass(LabeledInputView, [{
- key: 'select',
- value: function select() {
- this.inputView.select();
+ createClass(ViewListItemElement, [{
+ key: 'getFillerOffset',
+ value: function getFillerOffset() {
+ var hasOnlyLists = !this.isEmpty && (this.getChild(0).name == 'ul' || this.getChild(0).name == 'ol');
+
+ return this.isEmpty || hasOnlyLists ? 0 : null;
}
}]);
- return LabeledInputView;
-}(View);
-
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ return ViewListItemElement;
+}(ContainerElement);
/**
- * The text input view class.
- *
- * See {@link ui.input.InputText}.
+ * The list indent command. It is used by the {@link list.List list feature}.
*
- * @memberOf ui.input
- * @extends ui.View
+ * @memberOf list
+ * @namespace list.converters
*/
-var InputTextView = function (_View) {
- inherits(InputTextView, _View);
+// Helper function that creates a `` structure out of given `modelItem` model `listItem` element.
+// Then, it binds created view list item (LI) with model `listItem` element.
+// The function then returns created view list item (LI).
+function generateLiInUl(modelItem, mapper) {
+ var listType = modelItem.getAttribute('type') == 'numbered' ? 'ol' : 'ul';
+ var viewItem = new ViewListItemElement();
- /**
- * @inheritDoc
- */
- function InputTextView(locale) {
- classCallCheck(this, InputTextView);
+ var viewList = new ContainerElement(listType, null);
+ viewList.appendChildren(viewItem);
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(InputTextView).call(this, locale));
+ mapper.bindElements(modelItem, viewItem);
- var bind = _this.bindTemplate;
+ return viewItem;
+}
- _this.template = new Template({
- tag: 'input',
- attributes: {
- type: 'text',
- class: ['ck-input', 'ck-input-text'],
- id: bind.to('id')
- }
- });
+// Helper function that seeks for a sibling of given `modelItem` that is a `listItem` element and meets given criteria.
+// `options` object may contain one or more of given values (by default they are `false`):
+// `options.getNext` - whether next or previous siblings should be checked (default = previous)
+// `options.checkAllSiblings` - whether all siblings or just the first one should be checked (default = only one),
+// `options.sameIndent` - whether sought sibling should have same indent (default = no),
+// `options.biggerIndent` - whether sought sibling should have bigger indent (default = no).
+// Either `options.sameIndent` or `options.biggerIndent` should be set to `true`.
+function getSiblingListItem(modelItem, options) {
+ var direction = options.getNext ? 'nextSibling' : 'previousSibling';
+ var checkAllSiblings = !!options.checkAllSiblings;
+ var sameIndent = !!options.sameIndent;
+ var biggerIndent = !!options.biggerIndent;
- // Note: `value` cannot be an HTML attribute, because it doesn't change HTMLInputElement value once changed.
- _this.on('change:value', function (evt, propertyName, value) {
- return _this.element.value = value || '';
- });
+ var indent = modelItem.getAttribute('indent');
- /**
- * The value of the input.
- *
- * @observable
- * @member {String} ui.input.InputTextView#value
- */
+ var item = modelItem[direction];
- /**
- * The `id` attribute of the input (i.e. to pair with a `` element).
- *
- * @observable
- * @member {String} ui.input.InputTextView#id
- */
- return _this;
+ while (item && item.name == 'listItem') {
+ var itemIndent = item.getAttribute('indent');
+
+ if (sameIndent && indent == itemIndent || biggerIndent && indent < itemIndent) {
+ return item;
+ } else if (!checkAllSiblings || indent > itemIndent) {
+ return null;
+ }
+
+ item = item[direction];
+ }
+
+ return null;
+}
+
+// Helper function that takes two parameters, that are expected to be view list elements, and merges them.
+// The merge happen only if both parameters are UL or OL elements.
+function mergeViewLists(firstList, secondList) {
+ if (firstList && secondList && (firstList.name == 'ul' || firstList.name == 'ol') && firstList.name == secondList.name) {
+ viewWriter.mergeContainers(Position$1.createAfter(firstList));
+ }
+}
+
+// Helper function that takes model list item element `modelItem`, corresponding view list item element `injectedItem`
+// that is not added to the view and is inside a view list element (`ul` or `ol`) and is that's list only child.
+// The list is inserted at correct position (element breaking may be needed) and then merged with it's siblings.
+// See comments below to better understand the algorithm.
+function injectViewList(modelItem, injectedItem, mapper) {
+ var injectedList = injectedItem.parent;
+
+ // 1. Break after previous `listItem` if it has same or bigger indent.
+ var prevModelItem = getSiblingListItem(modelItem, { sameIndent: true, biggerIndent: true });
+
+ if (prevModelItem) {
+ var viewItem = mapper.toViewElement(prevModelItem);
+ var viewPosition = Position$1.createAfter(viewItem);
+ viewWriter.breakContainer(viewPosition);
+ }
+
+ // 2. Break after closest previous `listItem` sibling with same indent.
+ var sameIndentModelItem = getSiblingListItem(modelItem, { sameIndent: true, checkAllSiblings: true });
+ // Position between broken lists will be a place where new list is inserted.
+ // If there is nothing to break (`sameIndentModelItem` is falsy) it means that converted list item
+ // is (will be) the first list item.
+ var insertionPosition = void 0;
+
+ if (sameIndentModelItem) {
+ var _viewItem = mapper.toViewElement(sameIndentModelItem);
+ var _viewPosition = Position$1.createAfter(_viewItem);
+ insertionPosition = viewWriter.breakContainer(_viewPosition);
+ } else {
+ // If there is a list item before converted list item, it means that that list item has lower indent.
+ // In such case the created view list should be appended as a child of that item.
+ var prevSibling = modelItem.previousSibling;
+
+ if (prevSibling && prevSibling.name == 'listItem') {
+ insertionPosition = Position$1.createAt(mapper.toViewElement(prevSibling), 'end');
+ } else {
+ // This is the very first list item, use position mapping to get correct insertion position.
+ insertionPosition = mapper.toViewPosition(Position.createBefore(modelItem));
+ }
+ }
+
+ // 3. Append new UL/OL in position after breaking in step 2.
+ viewWriter.insert(insertionPosition, injectedList);
+
+ // 4. If next sibling is list item with bigger indent, append it's UL/OL to new LI.
+ var nextModelItem = getSiblingListItem(modelItem, { getNext: true, biggerIndent: true });
+ var nextViewItem = mapper.toViewElement(nextModelItem);
+
+ /* istanbul ignore if */ // Part of code connected with indenting that is not yet complete.
+ if (nextViewItem) {
+ var sourceRange = Range$2.createOn(nextViewItem.parent);
+ var targetPosition = Position$1.createAt(injectedItem, 'end');
+ viewWriter.move(sourceRange, targetPosition);
+ }
+
+ // 5. Merge new UL/OL with above and below items (ULs/OLs or LIs).
+ mergeViewLists(injectedList, injectedList.nextSibling);
+ mergeViewLists(injectedList.previousSibling, injectedList);
+}
+
+/**
+ * Model to view converter for `listItem` model element insertion.
+ *
+ * It creates `` (or ``) view structure out of `listItem` model element, inserts it at correct
+ * position, and merges the list with surrounding lists (if able).
+ *
+ * @see engine.conversion.ModelConversionDispatcher#event:insert
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Additional information about the change.
+ * @param {engine.conversion.ModelConsumable} consumable Values to consume.
+ * @param {Object} conversionApi Conversion interface.
+ */
+function modelViewInsertion(evt, data, consumable, conversionApi) {
+ if (!consumable.test(data.item, 'insert') || !consumable.test(data.item, 'addAttribute:type') || !consumable.test(data.item, 'addAttribute:indent')) {
+ return;
}
- /**
- * Moves the focus to the input and selects the value.
- */
-
+ consumable.consume(data.item, 'insert');
+ consumable.consume(data.item, 'addAttribute:type');
+ consumable.consume(data.item, 'addAttribute:indent');
- createClass(InputTextView, [{
- key: 'select',
- value: function select() {
- this.element.select();
- }
- }]);
- return InputTextView;
-}(View);
+ var modelItem = data.item;
+ var viewItem = generateLiInUl(modelItem, conversionApi.mapper);
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ injectViewList(modelItem, viewItem, conversionApi.mapper);
+}
/**
- * Handles native DOM `submit` event by preventing it and firing
- * the {ui.View} `submit` event, which can be then handled by the
- * parent controller.
+ * Model to view converter for `type` attribute change on `listItem` model element.
*
- * @param {Object} [options] Configuration options.
- * @param {ui.View} options.view The view to which this behavior should be added.
+ * This change means that ``s parent changes from `` to `` (or vice versa). This is accomplished by breaking
+ * view elements, changing their name and merging them.
+ *
+ * @see engine.conversion.ModelConversionDispatcher#event:changeAttribute
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Additional information about the change.
+ * @param {engine.conversion.ModelConsumable} consumable Values to consume.
+ * @param {Object} conversionApi Conversion interface.
*/
-function submitHandler(_ref) {
- var view = _ref.view;
+function modelViewChangeType(evt, data, consumable, conversionApi) {
+ if (!consumable.consume(data.item, 'changeAttribute:type')) {
+ return;
+ }
- view.listenTo(view.element, 'submit', function (evt, domEvt) {
- domEvt.preventDefault();
- view.fire('submit');
- }, { useCapture: true });
+ var viewItem = conversionApi.mapper.toViewElement(data.item);
+
+ // 1. Break the container after and before the list item.
+ // This will create a view list with one view list item -- the one that changed type.
+ viewWriter.breakContainer(Position$1.createBefore(viewItem));
+ viewWriter.breakContainer(Position$1.createAfter(viewItem));
+
+ // 2. Change name of the view list that holds the changed view item.
+ // We cannot just change name property, because that would not render properly.
+ var viewList = viewItem.parent;
+ var listName = data.attributeNewValue == 'numbered' ? 'ol' : 'ul';
+ viewList = viewWriter.rename(viewList, listName);
+
+ // 3. Merge the changed view list with other lists, if possible.
+ mergeViewLists(viewList, viewList.nextSibling);
+ mergeViewLists(viewList.previousSibling, viewList);
}
/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * Model to view converter for `listItem` model element remove.
+ *
+ * @see engine.conversion.ModelConversionDispatcher#event:remove
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Additional information about the change.
+ * @param {engine.conversion.ModelConsumable} consumable Values to consume.
+ * @param {Object} conversionApi Conversion interface.
*/
+function modelViewRemove(evt, data, consumable, conversionApi) {
+ if (!consumable.consume(data.item, 'remove')) {
+ return;
+ }
+
+ var viewItem = conversionApi.mapper.toViewElement(data.item);
+
+ // 1. Break the container after and before the list item.
+ // This will create a view list with one view list item -- the one that changed type.
+ viewWriter.breakContainer(Position$1.createBefore(viewItem));
+ viewWriter.breakContainer(Position$1.createAfter(viewItem));
+
+ // 2. Remove the UL that contains just the removed LI.
+ var viewList = viewItem.parent;
+ viewWriter.remove(Range$2.createOn(viewList));
+}
/**
- * The link form view controller class.
- *
- * See {@link link.ui.LinkForm}.
+ * Model to view converter for `listItem` model element move.
*
- * @memberOf link.ui
- * @extends ui.View
+ * @see engine.conversion.ModelConversionDispatcher#event:move
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Additional information about the change.
+ * @param {engine.conversion.ModelConsumable} consumable Values to consume.
+ * @param {Object} conversionApi Conversion interface.
*/
+function modelViewMove(evt, data, consumable, conversionApi) {
+ if (!consumable.consume(data.item, 'move')) {
+ return;
+ }
-var LinkFormView = function (_View) {
- inherits(LinkFormView, _View);
+ var viewItem = conversionApi.mapper.toViewElement(data.item);
- /**
- * @inheritDoc
- */
- function LinkFormView(locale) {
- classCallCheck(this, LinkFormView);
+ // 1. Break the container after and before the list item.
+ // This will create a view list with one view list item -- the one that changed type.
+ viewWriter.breakContainer(Position$1.createBefore(viewItem));
+ viewWriter.breakContainer(Position$1.createAfter(viewItem));
- /**
- * The url input view.
- *
- * @member {ui.input.labeled.LabeledInputView} link.ui.LinkFormView#urlInputView
- */
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(LinkFormView).call(this, locale));
+ // 2. Extract view list with changed view list item and merge "hole" possibly created by breaking and removing elements.
+ var viewList = viewItem.parent;
+ var viewListPrev = viewList.previousSibling;
+ var viewListNext = viewList.nextSibling;
- _this.urlInputView = new LabeledInputView(InputTextView, locale);
+ var insertionPosition = conversionApi.mapper.toViewPosition(data.targetPosition);
- /**
- * The save button view.
- *
- * @member {ui.button.ButtonView} link.ui.LinkFormView#saveButtonView
- */
- _this.saveButtonView = new ButtonView(locale);
+ if (insertionPosition.parent.name == 'ol' || insertionPosition.parent.name == 'ul') {
+ insertionPosition = viewWriter.breakContainer(insertionPosition);
+ }
- /**
- * The cancel button view.
- *
- * @member {ui.button.ButtonView} link.ui.LinkFormView#cancelButtonView
- */
- _this.cancelButtonView = new ButtonView(locale);
+ viewWriter.move(Range$2.createOn(viewList), insertionPosition);
- /**
- * The unlink button view.
- *
- * @member {ui.button.ButtonView} link.ui.LinkFormView#unlinkButtonView
- */
- _this.unlinkButtonView = new ButtonView(locale);
+ // No worries, merging will happen only if both elements exist and they are same type of lists.
+ mergeViewLists(viewListPrev, viewListNext);
+ mergeViewLists(viewList, viewList.nextSibling);
+ mergeViewLists(viewList.previousSibling, viewList);
+}
- Template.extend(_this.saveButtonView.template, {
- attributes: {
- class: ['ck-button-action']
- }
- });
+/**
+ * Model to view converter for `indent` attribute change on `listItem` model element.
+ *
+ * @see engine.conversion.ModelConversionDispatcher#event:changeAttribute
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Additional information about the change.
+ * @param {engine.conversion.ModelConsumable} consumable Values to consume.
+ * @param {Object} conversionApi Conversion interface.
+ */
+function modelViewChangeIndent(evt, data, consumable, conversionApi) {
+ /* istanbul ignore if */ // Part of code connected with indenting that is not yet complete.
+ if (!consumable.consume(data.item, 'changeAttribute:indent')) {
+ return;
+ }
- _this.template = new Template({
- tag: 'form',
+ var viewItem = conversionApi.mapper.toViewElement(data.item);
- attributes: {
- class: ['ck-link-form']
- },
+ // 1. Break the container after and before the list item.
+ // This will create a view list with one view list item -- the one that changed type.
+ viewWriter.breakContainer(Position$1.createBefore(viewItem));
+ viewWriter.breakContainer(Position$1.createAfter(viewItem));
- children: [_this.urlInputView, {
- tag: 'div',
+ // 2. Extract view list with changed view list item and merge "hole" possibly created by breaking and removing elements.
+ var viewList = viewItem.parent;
+ var viewListPrev = viewList.previousSibling;
- attributes: {
- class: ['ck-link-form__actions']
- },
+ viewWriter.remove(Range$2.createOn(viewList));
- children: [_this.saveButtonView, _this.cancelButtonView, _this.unlinkButtonView]
- }]
- });
+ // If there is no `viewListPrev` it means that the first item was indented which is an error.
+ mergeViewLists(viewListPrev, viewListPrev.nextSibling);
- submitHandler({
- view: _this
- });
- return _this;
- }
+ // 3. Inject view list like it is newly inserted.
+ injectViewList(data.item, viewItem, conversionApi.mapper);
+}
- return LinkFormView;
-}(View);
+/**
+ * A special model to view converter introduced by {@link list.List List feature}. This converter is fired for
+ * insert change of every model item, and should be fired before actual converter. The converter checks whether inserted
+ * model item is a non-`listItem` element. If it is, and it is inserted inside a view list, the converter breaks the
+ * list so the model element is inserted to the view parent element corresponding to its model parent element.
+ *
+ * The converter prevents such situations:
+ *
+ * // Model: // View:
+ * foo
+ *
+ * // After change: // Correct view guaranteed by this converter:
+ * foo xxx
+ * xxx // Instead of this wrong view state:
+ * bar
+ *
+ * @see engine.conversion.ModelConversionDispatcher#event:insert
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Additional information about the change.
+ * @param {engine.conversion.ModelConsumable} consumable Values to consume.
+ * @param {Object} conversionApi Conversion interface.
+ */
+function modelViewSplitOnInsert(evt, data, consumable, conversionApi) {
+ if (data.item.name != 'listItem') {
+ var viewPosition = conversionApi.mapper.toViewPosition(data.range.start);
+
+ // Break multiple ULs/OLs if there are.
+ while (viewPosition.parent.name == 'ul' || viewPosition.parent.name == 'ol') {
+ viewPosition = viewWriter.breakContainer(viewPosition);
+
+ /* istanbul ignore else */ // Part of code connected with indenting that is not yet complete.
+ if (viewPosition.parent.parent === null) {
+ break;
+ }
+
+ /* istanbul ignore next */ // Part of code connected with indenting that is not yet complete.
+ viewPosition = Position$1.createBefore(viewPosition.parent);
+ }
+ }
+}
/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * A special model to view converter introduced by {@link list.List List feature}. This converter takes care of
+ * merging view lists after something is removed or moved from near them.
+ *
+ * Example:
+ *
+ * // Model: // View:
+ * foo
+ * xxx xxx
+ * bar
+ *
+ * // After change: // Correct view guaranteed by this converter:
+ * foo
+ *
+ * @see engine.conversion.ModelConversionDispatcher#event:remove
+ * @see engine.conversion.ModelConversionDispatcher#event:move
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Additional information about the change.
+ * @param {engine.conversion.ModelConsumable} consumable Values to consume.
+ * @param {Object} conversionApi Conversion interface.
*/
+function modelViewMergeAfter(evt, data, consumable, conversionApi) {
+ var viewPosition = conversionApi.mapper.toViewPosition(data.sourcePosition);
+ var viewItemPrev = viewPosition.nodeBefore;
+ var viewItemNext = viewPosition.nodeAfter;
+
+ // Merge lists if something (remove, move) was done from inside of list.
+ // Merging will be done only if both items are view lists of the same type.
+ // The check is done inside the helper function.
+ mergeViewLists(viewItemPrev, viewItemNext);
+}
/**
- * The link feature. It introduces the Link and Unlink buttons and the Ctrl+K keystroke.
+ * View to model converter that converts view `` elements into `listItem` model elements.
*
- * It uses the {@link link.LinkEngine link engine feature}.
+ * To set correct values of `type` and `indent` attribute the converter:
+ * * checks ` `'s parent,
+ * * passes `data.indent` value when ` `'s sub-items are converted.
*
- * @memberOf link
- * @extends core.Feature
+ * @see engine.conversion.ViewConversionDispatcher#event:element
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Object containing conversion input and a placeholder for conversion output and possibly other values.
+ * @param {engine.conversion.ViewConsumable} consumable Values to consume.
+ * @param {Object} conversionApi Conversion interface to be used by callback.
*/
+function viewModelConverter(evt, data, consumable, conversionApi) {
+ if (consumable.consume(data.input, { name: true })) {
+ // 1. Create `listItem` model element.
+ var listItem = new Element('listItem');
-var Link = function (_Feature) {
- inherits(Link, _Feature);
-
- function Link() {
- classCallCheck(this, Link);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Link).apply(this, arguments));
- }
+ // 2. Handle `listItem` model element attributes.
+ data.indent = data.indent ? data.indent : 0;
- createClass(Link, [{
- key: 'init',
+ var type = data.input.parent.name == 'ul' ? 'bulleted' : 'numbered';
+ listItem.setAttribute('type', type);
+ listItem.setAttribute('indent', data.indent);
+ // 3. Handle ` ` children.
+ data.context.push(listItem);
- /**
- * @inheritDoc
- */
- value: function init() {
- this.editor.editing.view.addObserver(ClickObserver);
+ // `listItem`s created recursievly should have bigger indent.
+ data.indent++;
- /**
- * Balloon panel component to display the main UI.
- *
- * @member {link.ui.LinkBalloonPanel} link.Link#balloonPanel
- */
- this.balloonPanel = this._createBalloonPanel();
+ // `listItem`s will be kept in flat structure.
+ var items = [listItem];
- /**
- * The form component inside {@link link.Link#balloonPanel}.
- *
- * @member {link.ui.LinkForm} link.Link#form
- */
- this.form = this._createForm();
+ // Check all children of the converted ` `.
+ // At this point we assume there are no "whitespace" view text nodes in view list, between view list items.
+ // This should be handled by `` and `` converters.
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- // Create toolbar buttons.
- this._createToolbarLinkButton();
- this._createToolbarUnlinkButton();
- }
+ try {
+ for (var _iterator = data.input.getChildren()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var child = _step.value;
- /**
- * Creates a toolbar link button. Clicking this button will show
- * {@link link.Link#balloonPanel} attached to the selection.
- *
- * @private
- */
+ // Let's convert the child.
+ var converted = conversionApi.convertItem(child, consumable, data);
- }, {
- key: '_createToolbarLinkButton',
- value: function _createToolbarLinkButton() {
- var _this2 = this;
+ // If this is a view list element, we will convert it and concat the result (`listItem` model elements)
+ // with already gathered results (in `items` array). `converted` should be a `ModelDocumentFragment`.
+ if (child.name == 'ul' || child.name == 'ol') {
+ items = items.concat(Array.from(converted.getChildren()));
+ }
+ // If it was not a list it was a "regular" list item content. Just append it to `listItem`.
+ else {
+ listItem.appendChildren(converted);
+ }
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
- var editor = this.editor;
- var linkCommand = editor.commands.get('link');
- var t = editor.t;
+ data.indent--;
+ data.context.pop();
- // Create button model.
- var linkButtonModel = new Model({
- isEnabled: true,
- isOn: false,
- label: t('Link'),
- icon: 'link',
- keystroke: 'CTRL+K'
- });
+ /* istanbul ignore next */ // Part of code connected with indenting that is not yet complete.
+ data.output = data.output ? data.output.concat(items) : items;
+ }
+}
- // Bind button model to the command.
- linkButtonModel.bind('isEnabled').to(linkCommand, 'isEnabled');
+/**
+ * View to model converter for `` and `` view elements, that cleans the input view out of garbage.
+ * This is mostly to clean white spaces from between `` view elements inside the view list element, however also
+ * incorrect data can be cleared if the view was incorrect.
+ *
+ * @see engine.conversion.ViewConversionDispatcher#event:element
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Object containing conversion input and a placeholder for conversion output and possibly other values.
+ * @param {engine.conversion.ViewConsumable} consumable Values to consume.
+ */
+function cleanList(evt, data, consumable) {
+ if (consumable.test(data.input, { name: true })) {
+ // Caching children because when we start removing them iterating fails.
+ var children = Array.from(data.input.getChildren());
- // Show the panel on button click only when editor is focused.
- this.listenTo(linkButtonModel, 'execute', function () {
- return _this2._showPanel();
- });
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
- // Add link button to feature components.
- editor.ui.featureComponents.add('link', Button, ButtonView, linkButtonModel);
+ try {
+ for (var _iterator2 = children[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var child = _step2.value;
- // Handle `Ctrl+K` keystroke and show panel.
- editor.keystrokes.set('CTRL+K', function () {
- return _this2._showPanel();
- });
+ if (!child.name || child.name != 'li') {
+ child.remove();
+ }
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
}
+ }
+}
- /**
- * Creates a toolbar unlink button. Clicking this button will unlink
- * the selected link.
- *
- * @private
- */
-
- }, {
- key: '_createToolbarUnlinkButton',
- value: function _createToolbarUnlinkButton() {
- var editor = this.editor;
- var t = editor.t;
- var unlinkCommand = editor.commands.get('unlink');
-
- // Create the button model.
- var unlinkButtonModel = new Model({
- isEnabled: false,
- isOn: false,
- label: t('Unlink'),
- icon: 'unlink'
- });
-
- // Bind button model to the command.
- unlinkButtonModel.bind('isEnabled').to(unlinkCommand, 'isEnabled');
+/**
+ * Callback for model position to view position mapping for {@link engine.conversion.Mapper}. The callback fixes positions
+ * between `listItem` elements, that would be incorrectly mapped because of how list items are represented in model
+ * and view.
+ *
+ * @see engine.conversion.Mapper#event:modelToViewPosition
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Object containing additional data and placeholder for mapping result.
+ */
+function modelToViewPosition(evt, data) {
+ var modelPosition = data.modelPosition;
+ var mapper = data.mapper;
+ var nodeAfter = modelPosition.nodeAfter;
- // Execute unlink command and hide panel, if open.
- this.listenTo(unlinkButtonModel, 'execute', function () {
- editor.execute('unlink');
- });
+ // `listItem` elements are mapped with view, so positions inside them will be correctly mapped by default algorithm.
+ // Problem are positions between `listItem`s because they are incorrectly mapped to inside ` `. This is
+ // because of how view-to-model lengths work. What is important is that if a position is before a `listItem` and
+ // it is not a first `listItem`, the position has to be placed before corresponding ` `. If this is the first
+ // `listItem` position has to be before `` (this is default behavior).
+ if (nodeAfter && nodeAfter.name == 'listItem') {
+ var viewNode = mapper.toViewElement(nodeAfter);
- // Add unlink button to feature components.
- editor.ui.featureComponents.add('unlink', Button, ButtonView, unlinkButtonModel);
- }
+ if (viewNode && viewNode.index !== 0) {
+ data.viewPosition = Position$1.createBefore(viewNode);
- /**
- * Creates the {@link link.ui.LinkBalloonPanel} instance.
- *
- * @private
- * @returns {link.ui.LinkBalloonPanel} Link balloon panel instance.
- */
+ evt.stop();
+ }
+ }
+}
- }, {
- key: '_createBalloonPanel',
- value: function _createBalloonPanel() {
- var _this3 = this;
+/**
+ * Callback for view position to model position mapping for {@link engine.conversion.Mapper}. The callback fixes positions
+ * between `` elements, that would be incorrectly mapped because of how list items are represented in model
+ * and view.
+ *
+ * @see engine.conversion.Mapper#event:viewToModelPosition
+ * @param {utils.EventInfo} evt Object containing information about the fired event.
+ * @param {Object} data Object containing additional data and placeholder for mapping result.
+ */
+function viewToModelPosition(evt, data) {
+ var viewPosition = data.viewPosition;
+ var mapper = data.mapper;
+ var nodeAfter = viewPosition.nodeAfter;
+ var nodeBefore = viewPosition.nodeBefore;
- var editor = this.editor;
- var viewDocument = editor.editing.view;
+ var modelNode = void 0;
- // Create the balloon panel instance.
- var balloonPanel = new BalloonPanel(new Model({
- maxWidth: 300
- }), new BalloonPanelView(editor.locale));
+ if (nodeAfter) {
+ if (nodeAfter.name == 'ul' || nodeAfter.name == 'ol') {
+ // If the position is before view list, model position should be placed before `listItem`
+ // that is bound to the first ` ` of that view list.
+ // Default algorithm would work like this but only for top-level list.
+ modelNode = mapper.toModelElement(nodeAfter.getChild(0));
+ } else if (nodeAfter.name == 'li') {
+ // If the position is before view list item, just place model position before bound `listItem` element.
+ modelNode = mapper.toModelElement(nodeAfter);
+ }
- // Add balloonPanel.view#element to FocusTracker.
- // @TODO: Do it automatically ckeditor5-core#23
- editor.focusTracker.add(balloonPanel.view.element);
+ if (modelNode) {
+ data.modelPosition = Position.createBefore(modelNode);
+ }
+ } else if (nodeBefore) {
+ var viewNode = void 0;
- // Handle click on view document and show panel when selection is placed inside the link element.
- // Keep panel open until selection will be inside the same link element.
- this.listenTo(viewDocument, 'click', function () {
- var viewSelection = viewDocument.selection;
- var parentLink = getPositionParentLink(viewSelection.getFirstPosition());
+ // Find ` ` after which we want to place position.
+ // We want to find a ` ` that will be mapped to model `listItem` element. That `listItem` will
+ // be used as a reference point to evaluate model position.
+ /* istanbul ignore if */ // Part of code connected with indenting that is not yet complete.
+ if (nodeBefore.name == 'ul' || nodeBefore.name == 'ol') {
+ // If the position is before view list, take the last ` ` of that view list.
+ viewNode = nodeBefore.getChild(nodeBefore.childCount - 1);
+ } else if (nodeBefore.name == 'li') {
+ // If the position is before view list item, take that ` `.
+ viewNode = nodeBefore;
+ }
- if (viewSelection.isCollapsed && parentLink) {
- _this3._attachPanelToElement();
+ // Evaluate correct model position.
+ // At this stage we have a ` `. This ` ` may have nested ` `s inside. We will use `mapper`
+ // to obtain this ` `'s model length. Placing model position after that ` ` will be done
+ // by placing it before the bound `listItem` and moving by offset equal to ` `s length.
+ if (viewNode) {
+ modelNode = mapper.toModelElement(viewNode);
+ var offset = mapper.getModelLength(viewNode);
- _this3.listenTo(viewDocument, 'render', function () {
- var currentParentLink = getPositionParentLink(viewSelection.getFirstPosition());
+ data.modelPosition = Position.createBefore(modelNode).getShiftedBy(offset);
+ }
+ }
- if (!viewSelection.isCollapsed || parentLink !== currentParentLink) {
- _this3._hidePanel();
- } else {
- _this3._attachPanelToElement(parentLink);
- }
- });
+ // If we found a model position, stop the event.
+ if (data.modelPosition !== null) {
+ evt.stop();
+ }
+}
- _this3.listenTo(balloonPanel.view, 'change:isVisible', function () {
- return _this3.stopListening(viewDocument, 'render');
- });
- }
- });
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- // Close on `ESC` press.
- escPressHandler({
- controller: balloonPanel.view,
- model: balloonPanel.view,
- activeIf: 'isVisible',
- callback: function callback() {
- return _this3._hidePanel(true);
- }
- });
+/**
+ * The engine of the lists feature. It handles creating, editing and removing lists and list items.
+ * It registers the `numberedList`, `bulletedList`, `indentList` and `outdentList` commands.
+ *
+ * @memberOf list
+ * @extends core.Feature
+ */
- // Close on click outside of balloon panel element.
- clickOutsideHandler({
- controller: balloonPanel.view,
- model: balloonPanel.view,
- activeIf: 'isVisible',
- contextElement: balloonPanel.view.element,
- callback: function callback() {
- return _this3._hidePanel();
- }
- });
+var ListEngine = function (_Feature) {
+ inherits(ListEngine, _Feature);
- editor.ui.add('body', balloonPanel);
+ function ListEngine() {
+ classCallCheck(this, ListEngine);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(ListEngine).apply(this, arguments));
+ }
- return balloonPanel;
- }
+ createClass(ListEngine, [{
+ key: 'init',
/**
- * Creates the {@link link.ui.LinkForm} instance.
- *
- * @private
- * @returns {link.ui.LinkForm} Link form instance.
+ * @inheritDoc
*/
-
- }, {
- key: '_createForm',
- value: function _createForm() {
- var _this4 = this;
-
+ value: function init() {
var editor = this.editor;
- var formModel = new Model();
-
- formModel.bind('url').to(editor.commands.get('link'), 'value');
- var form = new LinkForm(formModel, new LinkFormView(editor.locale));
-
- // Execute link command after clicking on balloon panel `Link` button.
- this.listenTo(formModel, 'submit', function () {
- editor.execute('link', _this4.form.urlInput.value);
- _this4._hidePanel(true);
- });
-
- // Execute unlink command after clicking on balloon panel `Unlink` button.
- this.listenTo(formModel, 'unlink', function () {
- editor.execute('unlink');
- _this4._hidePanel(true);
- });
-
- // Hide balloon panel after clicking on balloon panel `Cancel` button.
- this.listenTo(formModel, 'cancel', function () {
- return _this4._hidePanel(true);
+ // Schema.
+ var schema = editor.document.schema;
+ schema.registerItem('listItem', '$block');
+ schema.allow({
+ name: 'listItem',
+ inside: '$root',
+ attributes: ['type', 'indent']
});
+ schema.requireAttributes('listItem', ['type', 'indent']);
- this.balloonPanel.add('content', form);
-
- return form;
- }
-
- /**
- * Shows {@link link.Link#balloonPanel LinkBalloonPanel} and attach to target element.
- * If selection is collapsed and is placed inside link element, then panel will be attached
- * to whole link element, otherwise will be attached to the selection.
- *
- * @private
- * @param {link.LinkElement} [parentLink] Target element.
- */
+ // Converters.
+ var data = editor.data;
+ var editing = editor.editing;
- }, {
- key: '_attachPanelToElement',
- value: function _attachPanelToElement(parentLink) {
- var viewDocument = this.editor.editing.view;
- var domEditableElement = viewDocument.domConverter.getCorrespondingDomElement(viewDocument.selection.editableElement);
- var targetLink = parentLink || getPositionParentLink(viewDocument.selection.getFirstPosition());
+ editing.mapper.on('modelToViewPosition', modelToViewPosition);
+ editing.mapper.on('viewToModelPosition', viewToModelPosition);
+ data.mapper.on('modelToViewPosition', modelToViewPosition);
- // When selection is inside link element, then attach panel to this element.
- if (targetLink) {
- this.balloonPanel.view.attachTo(viewDocument.domConverter.getCorrespondingDomElement(targetLink), domEditableElement);
- }
- // Otherwise attach panel to the selection.
- else {
- this.balloonPanel.view.attachTo(viewDocument.domConverter.viewRangeToDom(viewDocument.selection.getFirstRange()), domEditableElement);
- }
- }
+ editing.modelToView.on('insert', modelViewSplitOnInsert, { priority: 'high' });
+ editing.modelToView.on('insert:listItem', modelViewInsertion);
+ data.modelToView.on('insert', modelViewSplitOnInsert, { priority: 'high' });
+ data.modelToView.on('insert:listItem', modelViewInsertion);
- /**
- * Hides {@link link.Link#balloonPanel LinkBalloonPanel}.
- *
- * @private
- * @param {Boolean} [focusEditable=false] When `true` then editable focus will be restored on panel hide.
- */
+ // Only change converter is needed. List item's type attribute is required, so it's adding is handled when
+ // list item is added and you cannot remove it.
+ editing.modelToView.on('changeAttribute:type:listItem', modelViewChangeType);
+ data.modelToView.on('changeAttribute:type:listItem', modelViewChangeType);
- }, {
- key: '_hidePanel',
- value: function _hidePanel(focusEditable) {
- this.balloonPanel.view.hide();
+ editing.modelToView.on('remove:listItem', modelViewRemove);
+ editing.modelToView.on('remove', modelViewMergeAfter, { priority: 'low' });
+ data.modelToView.on('remove:listItem', modelViewRemove);
+ data.modelToView.on('remove', modelViewMergeAfter, { priority: 'low' });
- if (focusEditable) {
- this.editor.editing.view.focus();
- }
- }
+ editing.modelToView.on('move:listItem', modelViewMove);
+ editing.modelToView.on('move', modelViewMergeAfter, { priority: 'low' });
+ data.modelToView.on('move:listItem', modelViewMove);
+ data.modelToView.on('move', modelViewMergeAfter, { priority: 'low' });
- /**
- * Shows {@link link.Link#balloonPanel LinkBalloonPanel}.
- *
- * @private
- */
+ editing.modelToView.on('changeAttribute:indent:listItem', modelViewChangeIndent);
+ data.modelToView.on('changeAttribute:indent:listItem', modelViewChangeIndent);
- }, {
- key: '_showPanel',
- value: function _showPanel() {
- this._attachPanelToElement();
- this.form.urlInput.view.select();
- }
- }], [{
- key: 'requires',
+ data.viewToModel.on('element:li', viewModelConverter);
+ data.viewToModel.on('element:ul', cleanList, { priority: 'high' });
+ data.viewToModel.on('element:ol', cleanList, { priority: 'high' });
- /**
- * @inheritDoc
- */
- get: function get() {
- return [LinkEngine];
+ // Register commands for numbered and bulleted list.
+ editor.commands.set('numberedList', new ListCommand(editor, 'numbered'));
+ editor.commands.set('bulletedList', new ListCommand(editor, 'bulleted'));
+
+ // Register commands for indenting.
+ editor.commands.set('indentList', new IndentCommand(editor, 'forward'));
+ editor.commands.set('outdentList', new IndentCommand(editor, 'backward'));
}
}]);
- return Link;
+ return ListEngine;
}(Feature);
-function getPositionParentLink(position) {
- return position.parent.getAncestors().find(function (ancestor) {
- return ancestor instanceof LinkElement;
- });
-}
-
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
@@ -50544,48 +50053,264 @@ var MutationHandler = function () {
this.insertedCharacterCount -= length;
}
}]);
- return MutationHandler;
-}();
+ return MutationHandler;
+}();
+
+var safeKeycodes = [getCode('arrowUp'), getCode('arrowRight'), getCode('arrowDown'), getCode('arrowLeft'), 16, // Shift
+17, // Ctrl
+18, // Alt
+20, // CapsLock
+27, // Escape
+33, // PageUp
+34, // PageDown
+35, // Home
+36];
+
+// Function keys.
+for (var code = 112; code <= 135; code++) {
+ safeKeycodes.push(code);
+}
+
+// Returns `true` if a keystroke should not cause any content change caused by "typing".
+//
+// Note: This implementation is very simple and will need to be refined with time.
+//
+// @param {engine.view.observer.keyObserver.KeyEventData} keyData
+// @returns {Boolean}
+function isSafeKeystroke(keyData) {
+ // Keystrokes which contain Ctrl don't represent typing.
+ if (keyData.ctrlKey) {
+ return true;
+ }
+
+ return safeKeycodes.includes(keyData.keyCode);
+}
+
+// Helper function that compares whether two given view nodes are same. It is used in `diff` when it's passed an array
+// with child nodes.
+function compareChildNodes(oldChild, newChild) {
+ if (oldChild instanceof Text$2 && newChild instanceof Text$2) {
+ return oldChild.data === newChild.data;
+ } else {
+ return oldChild === newChild;
+ }
+}
+
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * The delete command. Used by the {@link typing.Delete delete feature} to handle the Delete and
+ * Backspace keys.
+ *
+ * @member typing
+ * @extends core.command.Command
+ */
+
+var DeleteCommand = function (_Command) {
+ inherits(DeleteCommand, _Command);
+
+ /**
+ * Creates an instance of the command.
+ *
+ * @param {core.editor.Editor} editor
+ * @param {'forward'|'backward'} direction The directionality of the delete describing in what direction it
+ * should consume the content when the selection is collapsed.
+ */
+ function DeleteCommand(editor, direction) {
+ classCallCheck(this, DeleteCommand);
+
+ /**
+ * The directionality of the delete describing in what direction it should
+ * consume the content when the selection is collapsed.
+ *
+ * @readonly
+ * @member {'forward'|'backward'} typing.DeleteCommand#direction
+ */
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DeleteCommand).call(this, editor));
+
+ _this.direction = direction;
+
+ /**
+ * Delete's change buffer used to group subsequent changes into batches.
+ *
+ * @readonly
+ * @private
+ * @member {typing.ChangeBuffer} typing.DeleteCommand#buffer
+ */
+ _this._buffer = new ChangeBuffer(editor.document, editor.config.get('undo.step'));
+ return _this;
+ }
+
+ /**
+ * Executes the delete command. Depending on whether the selection is collapsed or not, deletes its content
+ * or a piece of content in the {@link typing.DeleteCommand#direction defined direction}.
+ *
+ * @param {Object} [options] The command options.
+ * @param {'character'} [options.unit='character'] See {@link engine.model.composer.modifySelection}'s options.
+ */
+
+
+ createClass(DeleteCommand, [{
+ key: '_doExecute',
+ value: function _doExecute() {
+ var _this2 = this;
+
+ var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
+
+ var doc = this.editor.document;
+
+ doc.enqueueChanges(function () {
+ var selection = Selection.createFromSelection(doc.selection);
+
+ // Try to extend the selection in the specified direction.
+ if (selection.isCollapsed) {
+ doc.composer.modifySelection(selection, { direction: _this2.direction, unit: options.unit });
+ }
+
+ // If selection is still collapsed, then there's nothing to delete.
+ if (selection.isCollapsed) {
+ return;
+ }
+
+ var changeCount = 0;
+
+ selection.getFirstRange().getMinimalFlatRanges().forEach(function (range) {
+ changeCount += count(range.getWalker({ singleCharacters: true, ignoreElementEnd: true, shallow: true }));
+ });
+
+ doc.composer.deleteContents(_this2._buffer.batch, selection, { merge: true });
+ _this2._buffer.input(changeCount);
+
+ doc.selection.setRanges(selection.getRanges(), selection.isBackward);
+ });
+ }
+ }]);
+ return DeleteCommand;
+}(Command);
+
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * Delete observer introduces the {@link engine.view.Document#delete} event.
+ *
+ * @memberOf typing
+ * @extends engine.view.observer.Observer
+ */
+
+var DeleteObserver = function (_Observer) {
+ inherits(DeleteObserver, _Observer);
+
+ function DeleteObserver(document) {
+ classCallCheck(this, DeleteObserver);
+
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DeleteObserver).call(this, document));
+
+ document.on('keydown', function (evt, data) {
+ var deleteData = {};
+
+ if (data.keyCode == keyCodes.delete) {
+ deleteData.direction = 'forward';
+ deleteData.unit = 'character';
+ } else if (data.keyCode == keyCodes.backspace) {
+ deleteData.direction = 'backward';
+ deleteData.unit = 'codePoint';
+ } else {
+ return;
+ }
+
+ deleteData.unit = data.altKey ? 'word' : deleteData.unit;
+
+ document.fire('delete', new DomEventData(document, data.domEvent, deleteData));
+ });
+ return _this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+
+
+ createClass(DeleteObserver, [{
+ key: 'observe',
+ value: function observe() {}
+ }]);
+ return DeleteObserver;
+}(Observer);
+
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * The delete and backspace feature. Handles the Delete and Backspace keys in the editor.
+ *
+ * @memberOf typing
+ * @extends core.Feature
+ */
+
+var Delete = function (_Feature) {
+ inherits(Delete, _Feature);
+
+ function Delete() {
+ classCallCheck(this, Delete);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Delete).apply(this, arguments));
+ }
+
+ createClass(Delete, [{
+ key: 'init',
+ value: function init() {
+ var editor = this.editor;
+ var editingView = editor.editing.view;
+
+ editingView.addObserver(DeleteObserver);
+
+ editor.commands.set('forwardDelete', new DeleteCommand(editor, 'forward'));
+ editor.commands.set('delete', new DeleteCommand(editor, 'backward'));
+
+ this.listenTo(editingView, 'delete', function (evt, data) {
+ editor.execute(data.direction == 'forward' ? 'forwardDelete' : 'delete', { unit: data.unit });
+ data.preventDefault();
+ });
+ }
+ }]);
+ return Delete;
+}(Feature);
-var safeKeycodes = [getCode('arrowUp'), getCode('arrowRight'), getCode('arrowDown'), getCode('arrowLeft'), 16, // Shift
-17, // Ctrl
-18, // Alt
-20, // CapsLock
-27, // Escape
-33, // PageUp
-34, // PageDown
-35, // Home
-36];
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
-// Function keys.
-for (var code = 112; code <= 135; code++) {
- safeKeycodes.push(code);
-}
+/**
+ * The typing feature. Handles typing.
+ *
+ * @memberOf typing
+ * @extends core.Feature
+ */
-// Returns `true` if a keystroke should not cause any content change caused by "typing".
-//
-// Note: This implementation is very simple and will need to be refined with time.
-//
-// @param {engine.view.observer.keyObserver.KeyEventData} keyData
-// @returns {Boolean}
-function isSafeKeystroke(keyData) {
- // Keystrokes which contain Ctrl don't represent typing.
- if (keyData.ctrlKey) {
- return true;
- }
+var Typing = function (_Feature) {
+ inherits(Typing, _Feature);
- return safeKeycodes.includes(keyData.keyCode);
-}
+ function Typing() {
+ classCallCheck(this, Typing);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Typing).apply(this, arguments));
+ }
-// Helper function that compares whether two given view nodes are same. It is used in `diff` when it's passed an array
-// with child nodes.
-function compareChildNodes(oldChild, newChild) {
- if (oldChild instanceof Text$2 && newChild instanceof Text$2) {
- return oldChild.data === newChild.data;
- } else {
- return oldChild === newChild;
- }
-}
+ createClass(Typing, null, [{
+ key: 'requires',
+ get: function get() {
+ return [Input, Delete];
+ }
+ }]);
+ return Typing;
+}(Feature);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -50593,215 +50318,321 @@ function compareChildNodes(oldChild, newChild) {
*/
/**
- * The delete command. Used by the {@link typing.Delete delete feature} to handle the Delete and
- * Backspace keys.
+ * Base class for undo feature commands: {@link undo.UndoCommand} and {@link undo.RedoCommand}.
*
- * @member typing
- * @extends core.command.Command
+ * @protected
+ * @memberOf undo
*/
-var DeleteCommand = function (_Command) {
- inherits(DeleteCommand, _Command);
+var BaseCommand = function (_Command) {
+ inherits(BaseCommand, _Command);
- /**
- * Creates an instance of the command.
- *
- * @param {core.editor.Editor} editor
- * @param {'forward'|'backward'} direction The directionality of the delete describing in what direction it
- * should consume the content when the selection is collapsed.
- */
- function DeleteCommand(editor, direction) {
- classCallCheck(this, DeleteCommand);
+ function BaseCommand(editor) {
+ classCallCheck(this, BaseCommand);
/**
- * The directionality of the delete describing in what direction it should
- * consume the content when the selection is collapsed.
+ * Stack of items stored by the command. These are pairs of:
*
- * @readonly
- * @member {'forward'|'backward'} typing.DeleteCommand#direction
+ * * {@link engine.model.Batch batch} saved by the command,
+ * * {@link engine.model.Selection selection} state at the moment of saving the batch.
+ *
+ * @protected
+ * @member {Array} undo.BaseCommand#_stack
*/
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DeleteCommand).call(this, editor));
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(BaseCommand).call(this, editor));
- _this.direction = direction;
+ _this._stack = [];
/**
- * Delete's change buffer used to group subsequent changes into batches.
+ * Stores all batches that were created by this command.
*
- * @readonly
- * @private
- * @member {typing.ChangeBuffer} typing.DeleteCommand#buffer
+ * @protected
+ * @member {WeakSet.} undo.BaseCommand#_createdBatches
*/
- _this._buffer = new ChangeBuffer(editor.document, editor.config.get('undo.step'));
+ _this._createdBatches = new WeakSet();
+
+ // Refresh state, so command is inactive just after initialization.
+ _this.refreshState();
return _this;
}
/**
- * Executes the delete command. Depending on whether the selection is collapsed or not, deletes its content
- * or a piece of content in the {@link typing.DeleteCommand#direction defined direction}.
+ * Stores a batch in the command, together with the selection state of the {@link engine.model.Document document}
+ * created by the editor which this command is registered to.
*
- * @param {Object} [options] The command options.
- * @param {'character'} [options.unit='character'] See {@link engine.model.composer.modifySelection}'s options.
+ * @param {engine.model.Batch} batch The batch to add.
*/
- createClass(DeleteCommand, [{
- key: '_doExecute',
- value: function _doExecute() {
- var _this2 = this;
+ createClass(BaseCommand, [{
+ key: 'addBatch',
+ value: function addBatch(batch) {
+ var selection = {
+ ranges: Array.from(this.editor.document.selection.getRanges()),
+ isBackward: this.editor.document.selection.isBackward
+ };
- var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
+ this._stack.push({ batch: batch, selection: selection });
+ this.refreshState();
+ }
- var doc = this.editor.document;
+ /**
+ * Removes all items from the stack.
+ */
- doc.enqueueChanges(function () {
- var selection = Selection.createFromSelection(doc.selection);
+ }, {
+ key: 'clearStack',
+ value: function clearStack() {
+ this._stack = [];
+ this.refreshState();
+ }
- // Try to extend the selection in the specified direction.
- if (selection.isCollapsed) {
- doc.composer.modifySelection(selection, { direction: _this2.direction, unit: options.unit });
- }
+ /**
+ * @inheritDoc
+ */
- // If selection is still collapsed, then there's nothing to delete.
- if (selection.isCollapsed) {
- return;
- }
+ }, {
+ key: '_checkEnabled',
+ value: function _checkEnabled() {
+ return this._stack.length > 0;
+ }
- var changeCount = 0;
+ /**
+ * Restores the {@link engine.model.Document#selection document selection} state after a batch was undone.
+ *
+ * @protected
+ * @param {Array.} ranges Ranges to be restored.
+ * @param {Boolean} isBackward A flag describing whether the restored range was selected forward or backward.
+ */
- selection.getFirstRange().getMinimalFlatRanges().forEach(function (range) {
- changeCount += count(range.getWalker({ singleCharacters: true, ignoreElementEnd: true, shallow: true }));
- });
+ }, {
+ key: '_restoreSelection',
+ value: function _restoreSelection(ranges, isBackward, deltas) {
+ var document = this.editor.document;
- doc.composer.deleteContents(_this2._buffer.batch, selection, { merge: true });
- _this2._buffer.input(changeCount);
+ // This will keep the transformed selection ranges.
+ var selectionRanges = [];
- doc.selection.setRanges(selection.getRanges(), selection.isBackward);
- });
+ // Transform all ranges from the restored selection.
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var range = _step.value;
+
+ var transformedRanges = transformSelectionRange(range, deltas);
+
+ // For each `range` from `ranges`, we take only one transformed range.
+ // This is because we want to prevent situation where single-range selection
+ // got transformed to multi-range selection. We will take the first range that
+ // is not in the graveyard.
+ var transformedRange = transformedRanges.find(function (range) {
+ return range.start.root != document.graveyard;
+ });
+
+ // `transformedRange` might be `undefined` if transformed range ended up in graveyard.
+ if (transformedRange) {
+ selectionRanges.push(transformedRange);
+ }
+ }
+
+ // `selectionRanges` may be empty if all ranges ended up in graveyard. If that is the case, do not restore selection.
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+
+ if (selectionRanges.length) {
+ document.selection.setRanges(selectionRanges, isBackward);
+ }
}
}]);
- return DeleteCommand;
+ return BaseCommand;
}(Command);
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+function transformDelta(setToTransform, setToTransformBy) {
+ var isStrong = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
-/**
- * Delete observer introduces the {@link engine.view.Document#delete} event.
- *
- * @memberOf typing
- * @extends engine.view.observer.Observer
- */
+ var results = [];
-var DeleteObserver = function (_Observer) {
- inherits(DeleteObserver, _Observer);
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
- function DeleteObserver(document) {
- classCallCheck(this, DeleteObserver);
+ try {
+ for (var _iterator2 = setToTransform[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var toTransform = _step2.value;
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(DeleteObserver).call(this, document));
+ var to = [toTransform];
+
+ for (var t = 0; t < to.length; t++) {
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
- document.on('keydown', function (evt, data) {
- var deleteData = {};
+ try {
+ for (var _iterator3 = setToTransformBy[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var transformBy = _step3.value;
- if (data.keyCode == keyCodes.delete) {
- deleteData.direction = 'forward';
- deleteData.unit = 'character';
- } else if (data.keyCode == keyCodes.backspace) {
- deleteData.direction = 'backward';
- deleteData.unit = 'codePoint';
- } else {
- return;
+ var transformed = transform$1(to[t], transformBy, isStrong);
+ to.splice.apply(to, [t, 1].concat(toConsumableArray(transformed)));
+ t = t - 1 + transformed.length;
+ }
+ } catch (err) {
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion3 && _iterator3.return) {
+ _iterator3.return();
+ }
+ } finally {
+ if (_didIteratorError3) {
+ throw _iteratorError3;
+ }
+ }
+ }
}
- deleteData.unit = data.altKey ? 'word' : deleteData.unit;
-
- document.fire('delete', new DomEventData(document, data.domEvent, deleteData));
- });
- return _this;
+ results = results.concat(to);
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
}
- /**
- * @inheritDoc
- */
+ return results;
+}
+// Transforms given range `range` by deltas from `document` history, starting from a delta with given `baseVersion`.
+// Returns an array containing one or more ranges, which are result of the transformation.
+function transformSelectionRange(range, deltas) {
+ // The range will be transformed by history deltas that happened after the selection got stored.
+ // Note, that at this point, the document history is already updated by undo command execution. We will
+ // not transform the range by deltas that got undone or their reversing counterparts.
+ var transformed = transformRangesByDeltas([range], deltas);
- createClass(DeleteObserver, [{
- key: 'observe',
- value: function observe() {}
- }]);
- return DeleteObserver;
-}(Observer);
+ // After `range` got transformed, we have an array of ranges. Some of those
+ // ranges may be "touching" -- they can be next to each other and could be merged.
+ // First, we have to sort those ranges because they don't have to be in an order.
+ transformed.sort(function (a, b) {
+ return a.start.isBefore(b.start) ? -1 : 1;
+ });
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ // Then, we check if two consecutive ranges are touching.
+ for (var i = 1; i < transformed.length; i++) {
+ var a = transformed[i - 1];
+ var b = transformed[i];
-/**
- * The delete and backspace feature. Handles the Delete and Backspace keys in the editor.
- *
- * @memberOf typing
- * @extends core.Feature
- */
+ if (a.end.isTouching(b.start)) {
+ a.end = b.end;
+ transformed.splice(i, 1);
+ i--;
+ }
+ }
-var Delete = function (_Feature) {
- inherits(Delete, _Feature);
+ return transformed;
+}
- function Delete() {
- classCallCheck(this, Delete);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Delete).apply(this, arguments));
- }
+// Transforms given set of `ranges` by given set of `deltas`. Returns transformed `ranges`.
+function transformRangesByDeltas(ranges, deltas) {
+ var _iteratorNormalCompletion4 = true;
+ var _didIteratorError4 = false;
+ var _iteratorError4 = undefined;
- createClass(Delete, [{
- key: 'init',
- value: function init() {
- var editor = this.editor;
- var editingView = editor.editing.view;
+ try {
+ for (var _iterator4 = deltas[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
+ var delta = _step4.value;
+ var _iteratorNormalCompletion5 = true;
+ var _didIteratorError5 = false;
+ var _iteratorError5 = undefined;
- editingView.addObserver(DeleteObserver);
+ try {
+ for (var _iterator5 = delta.operations[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
+ var operation = _step5.value;
- editor.commands.set('forwardDelete', new DeleteCommand(editor, 'forward'));
- editor.commands.set('delete', new DeleteCommand(editor, 'backward'));
+ // We look through all operations from all deltas.
- this.listenTo(editingView, 'delete', function (evt, data) {
- editor.execute(data.direction == 'forward' ? 'forwardDelete' : 'delete', { unit: data.unit });
- data.preventDefault();
- });
- }
- }]);
- return Delete;
-}(Feature);
+ for (var i = 0; i < ranges.length; i++) {
+ // We transform every range by every operation.
+ var result = void 0;
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ switch (operation.type) {
+ case 'insert':
+ result = ranges[i]._getTransformedByInsertion(operation.position, operation.nodes.maxOffset, true);
+ break;
-/**
- * The typing feature. Handles typing.
- *
- * @memberOf typing
- * @extends core.Feature
- */
+ case 'move':
+ case 'remove':
+ case 'reinsert':
+ result = ranges[i]._getTransformedByMove(operation.sourcePosition, operation.targetPosition, operation.howMany, true);
+ break;
+ }
-var Typing = function (_Feature) {
- inherits(Typing, _Feature);
+ // If we have a transformation result, we substitute transformed range with it in `transformed` array.
+ // Keep in mind that the result is an array and may contain multiple ranges.
+ if (result) {
+ ranges.splice.apply(ranges, [i, 1].concat(toConsumableArray(result)));
- function Typing() {
- classCallCheck(this, Typing);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Typing).apply(this, arguments));
- }
+ // Fix iterator.
+ i = i + result.length - 1;
+ }
+ }
+ }
+ } catch (err) {
+ _didIteratorError5 = true;
+ _iteratorError5 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion5 && _iterator5.return) {
+ _iterator5.return();
+ }
+ } finally {
+ if (_didIteratorError5) {
+ throw _iteratorError5;
+ }
+ }
+ }
+ }
+ } catch (err) {
+ _didIteratorError4 = true;
+ _iteratorError4 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion4 && _iterator4.return) {
+ _iterator4.return();
+ }
+ } finally {
+ if (_didIteratorError4) {
+ throw _iteratorError4;
+ }
+ }
+ }
- createClass(Typing, null, [{
- key: 'requires',
- get: function get() {
- return [Input, Delete];
- }
- }]);
- return Typing;
-}(Feature);
+ return ranges;
+}
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -50809,321 +50640,331 @@ var Typing = function (_Feature) {
*/
/**
- * Base class for undo feature commands: {@link undo.UndoCommand} and {@link undo.RedoCommand}.
+ * The undo command stores {@link engine.model.Batch batches} applied to the {@link engine.model.Document document}
+ * and is able to undo a batch by reversing it and transforming by other batches from {@link engine.model.Document#history history}
+ * that happened after the reversed batch.
+ *
+ * The undo command also takes care of restoring the {@link engine.model.Document#selection document selection} to the state before
+ * the undone batch was applied.
*
- * @protected
* @memberOf undo
+ * @extends undo.BaseCommand
*/
-var BaseCommand = function (_Command) {
- inherits(BaseCommand, _Command);
-
- function BaseCommand(editor) {
- classCallCheck(this, BaseCommand);
+var UndoCommand = function (_BaseCommand) {
+ inherits(UndoCommand, _BaseCommand);
- /**
- * Stack of items stored by the command. These are pairs of:
- *
- * * {@link engine.model.Batch batch} saved by the command,
- * * {@link engine.model.Selection selection} state at the moment of saving the batch.
- *
- * @protected
- * @member {Array} undo.BaseCommand#_stack
- */
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(BaseCommand).call(this, editor));
+ function UndoCommand() {
+ classCallCheck(this, UndoCommand);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(UndoCommand).apply(this, arguments));
+ }
- _this._stack = [];
+ createClass(UndoCommand, [{
+ key: '_doExecute',
/**
- * Stores all batches that were created by this command.
+ * Executes the command. This method reverts a {@link engine.model.Batch batch} added to the command's stack, transforms
+ * and applies the reverted version on the {@link engine.model.Document document} and removes the batch from the stack.
+ * Then, it restores the {@link engine.model.Document#selection document selection}.
*
* @protected
- * @member {WeakSet.} undo.BaseCommand#_createdBatches
+ * @fires undo.UndoCommand#event:revert
+ * @param {engine.model.Batch} [batch] A batch that should be undone. If not set, the last added batch will be undone.
*/
- _this._createdBatches = new WeakSet();
+ value: function _doExecute() {
+ var _this2 = this;
- // Refresh state, so command is inactive just after initialization.
- _this.refreshState();
- return _this;
- }
+ var batch = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0];
- /**
- * Stores a batch in the command, together with the selection state of the {@link engine.model.Document document}
- * created by the editor which this command is registered to.
- *
- * @param {engine.model.Batch} batch The batch to add.
- */
+ // If batch is not given, set `batchIndex` to the last index in command stack.
+ var batchIndex = batch ? this._stack.findIndex(function (a) {
+ return a.batch == batch;
+ }) : this._stack.length - 1;
+ var item = this._stack.splice(batchIndex, 1)[0];
- createClass(BaseCommand, [{
- key: 'addBatch',
- value: function addBatch(batch) {
- var selection = {
- ranges: Array.from(this.editor.document.selection.getRanges()),
- isBackward: this.editor.document.selection.isBackward
- };
+ // All changes has to be done in one `enqueueChanges` callback so other listeners will not
+ // step between consecutive deltas, or won't do changes to the document before selection is properly restored.
+ this.editor.document.enqueueChanges(function () {
+ var undoingBatch = _this2._undo(item.batch);
- this._stack.push({ batch: batch, selection: selection });
- this.refreshState();
- }
+ var deltas = _this2.editor.document.history.getDeltas(item.batch.baseVersion);
+ _this2._restoreSelection(item.selection.ranges, item.selection.isBackward, deltas);
- /**
- * Removes all items from the stack.
- */
+ _this2.fire('revert', item.batch, undoingBatch);
+ });
- }, {
- key: 'clearStack',
- value: function clearStack() {
- this._stack = [];
this.refreshState();
}
/**
- * @inheritDoc
+ * Returns an index in {@link undo.BaseCommand#_stack} pointing to the item that is storing a batch that has a given
+ * {@link engine.model.Batch#baseVersion}.
+ *
+ * @private
+ * @param {Number} baseVersion The base version of the batch to find.
+ * @returns {Number|null}
*/
}, {
- key: '_checkEnabled',
- value: function _checkEnabled() {
- return this._stack.length > 0;
+ key: '_getItemIndexFromBaseVersion',
+ value: function _getItemIndexFromBaseVersion(baseVersion) {
+ for (var i = 0; i < this._stack.length; i++) {
+ if (this._stack[i].batch.baseVersion == baseVersion) {
+ return i;
+ }
+ }
+
+ return null;
}
/**
- * Restores the {@link engine.model.Document#selection document selection} state after a batch was undone.
+ * Undoes a batch by reversing a batch from history, transforming that reversed batch and applying it. This is
+ * a helper method for {@link undo.UndoCommand#_doExecute}.
*
- * @protected
- * @param {Array.} ranges Ranges to be restored.
- * @param {Boolean} isBackward A flag describing whether the restored range was selected forward or backward.
+ * @private
+ * @param {engine.model.Batch} batchToUndo A batch whose deltas will be reversed, transformed and applied.
*/
}, {
- key: '_restoreSelection',
- value: function _restoreSelection(ranges, isBackward, deltas) {
+ key: '_undo',
+ value: function _undo(batchToUndo) {
var document = this.editor.document;
- // This will keep the transformed selection ranges.
- var selectionRanges = [];
+ // All changes done by the command execution will be saved as one batch.
+ var undoingBatch = document.batch();
+ this._createdBatches.add(undoingBatch);
- // Transform all ranges from the restored selection.
+ var history = document.history;
+ var deltasToUndo = batchToUndo.deltas.slice();
+ deltasToUndo.reverse();
+
+ // We will process each delta from `batchToUndo`, in reverse order. If there was deltas A, B and C in undone batch,
+ // we need to revert them in reverse order, so first reverse C, then B, then A.
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
- for (var _iterator = ranges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var range = _step.value;
+ for (var _iterator = deltasToUndo[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var deltaToUndo = _step.value;
- var transformedRanges = transformSelectionRange(range, deltas);
+ // Keep in mind that all algorithms return arrays. That's because the transformation might result in multiple
+ // deltas, so we need arrays to handle them anyway. To simplify algorithms, it is better to always have arrays
+ // in mind. For simplicity reasons, we will use singular form in descriptions and names.
+ var baseVersion = deltaToUndo.baseVersion;
+ var nextBaseVersion = baseVersion + deltaToUndo.operations.length;
- // For each `range` from `ranges`, we take only one transformed range.
- // This is because we want to prevent situation where single-range selection
- // got transformed to multi-range selection. We will take the first range that
- // is not in the graveyard.
- var transformedRange = transformedRanges.find(function (range) {
- return range.start.root != document.graveyard;
- });
+ // 1. Get updated version of the delta from the history.
+ // Batch stored in the undo command might have an outdated version of the delta that should be undone.
+ // To prevent errors, we will take an updated version of it from the history, basing on delta's `baseVersion`.
+ var updatedDeltaToUndo = history.getDelta(baseVersion);
- // `transformedRange` might be `undefined` if transformed range ended up in graveyard.
- if (transformedRange) {
- selectionRanges.push(transformedRange);
+ // This is a safe valve in case of not finding delta to undo in history. This may come up if that delta
+ // got updated into no deltas, or removed from history.
+ if (updatedDeltaToUndo === null) {
+ continue;
}
- }
- // `selectionRanges` may be empty if all ranges ended up in graveyard. If that is the case, do not restore selection.
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
+ // 2. Reverse delta from the history.
+ updatedDeltaToUndo.reverse();
+ var reversedDelta = [];
+
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+
+ try {
+ for (var _iterator2 = updatedDeltaToUndo[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var delta = _step2.value;
+
+ reversedDelta.push(delta.getReversed());
+ }
+
+ // Stores history deltas transformed by `deltaToUndo`. Will be used later for updating document history.
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
+ }
+ }
}
- }
- }
- if (selectionRanges.length) {
- document.selection.setRanges(selectionRanges, isBackward);
- }
- }
- }]);
- return BaseCommand;
-}(Command);
+ var updatedHistoryDeltas = {};
-function transformDelta(setToTransform, setToTransformBy) {
- var isStrong = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2];
+ // 3. Transform reversed delta by history deltas that happened after delta to undo. We have to bring
+ // reversed delta to the current state of document. While doing this, we will also update history deltas
+ // to the state which "does not remember" delta that we undo.
+ var _iteratorNormalCompletion3 = true;
+ var _didIteratorError3 = false;
+ var _iteratorError3 = undefined;
- var results = [];
+ try {
+ for (var _iterator3 = history.getDeltas(nextBaseVersion)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var historyDelta = _step3.value;
- var _iteratorNormalCompletion2 = true;
- var _didIteratorError2 = false;
- var _iteratorError2 = undefined;
+ // 3.1. Transform selection range stored with history batch by reversed delta.
+ // It is important to keep stored selection ranges updated. As we are removing and updating deltas in the history,
+ // selection ranges would base on outdated history state.
+ var itemIndex = this._getItemIndexFromBaseVersion(historyDelta.baseVersion);
- try {
- for (var _iterator2 = setToTransform[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- var toTransform = _step2.value;
+ // `itemIndex` will be `null` for `historyDelta` if it is not the first delta in it's batch.
+ // This is fine, because we want to transform each selection only once, before transforming reversed delta
+ // by the first delta of the batch connected with the ranges.
+ if (itemIndex !== null) {
+ this._stack[itemIndex].selection.ranges = transformRangesByDeltas(this._stack[itemIndex].selection.ranges, reversedDelta);
+ }
- var to = [toTransform];
+ // 3.2. Transform history delta by reversed delta. We need this to update document history.
+ var updatedHistoryDelta = transformDelta([historyDelta], reversedDelta, false);
- for (var t = 0; t < to.length; t++) {
- var _iteratorNormalCompletion3 = true;
- var _didIteratorError3 = false;
- var _iteratorError3 = undefined;
+ // 3.3. Transform reversed delta by history delta (in state before transformation above).
+ reversedDelta = transformDelta(reversedDelta, [historyDelta], true);
- try {
- for (var _iterator3 = setToTransformBy[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
- var transformBy = _step3.value;
+ // 3.4. Store updated history delta. Later, it will be updated in `history`.
+ if (!updatedHistoryDeltas[historyDelta.baseVersion]) {
+ updatedHistoryDeltas[historyDelta.baseVersion] = [];
+ }
- var transformed = transform$1(to[t], transformBy, isStrong);
- to.splice.apply(to, [t, 1].concat(toConsumableArray(transformed)));
- t = t - 1 + transformed.length;
- }
- } catch (err) {
- _didIteratorError3 = true;
- _iteratorError3 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion3 && _iterator3.return) {
- _iterator3.return();
- }
- } finally {
- if (_didIteratorError3) {
- throw _iteratorError3;
+ updatedHistoryDeltas[historyDelta.baseVersion] = updatedHistoryDeltas[historyDelta.baseVersion].concat(updatedHistoryDelta);
}
- }
- }
- }
-
- results = results.concat(to);
- }
- } catch (err) {
- _didIteratorError2 = true;
- _iteratorError2 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion2 && _iterator2.return) {
- _iterator2.return();
- }
- } finally {
- if (_didIteratorError2) {
- throw _iteratorError2;
- }
- }
- }
- return results;
-}
+ // 4. After reversed delta has been transformed by all history deltas, apply it.
+ } catch (err) {
+ _didIteratorError3 = true;
+ _iteratorError3 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion3 && _iterator3.return) {
+ _iterator3.return();
+ }
+ } finally {
+ if (_didIteratorError3) {
+ throw _iteratorError3;
+ }
+ }
+ }
-// Transforms given range `range` by deltas from `document` history, starting from a delta with given `baseVersion`.
-// Returns an array containing one or more ranges, which are result of the transformation.
-function transformSelectionRange(range, deltas) {
- // The range will be transformed by history deltas that happened after the selection got stored.
- // Note, that at this point, the document history is already updated by undo command execution. We will
- // not transform the range by deltas that got undone or their reversing counterparts.
- var transformed = transformRangesByDeltas([range], deltas);
+ var _iteratorNormalCompletion4 = true;
+ var _didIteratorError4 = false;
+ var _iteratorError4 = undefined;
- // After `range` got transformed, we have an array of ranges. Some of those
- // ranges may be "touching" -- they can be next to each other and could be merged.
- // First, we have to sort those ranges because they don't have to be in an order.
- transformed.sort(function (a, b) {
- return a.start.isBefore(b.start) ? -1 : 1;
- });
+ try {
+ for (var _iterator4 = reversedDelta[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
+ var _delta = _step4.value;
- // Then, we check if two consecutive ranges are touching.
- for (var i = 1; i < transformed.length; i++) {
- var a = transformed[i - 1];
- var b = transformed[i];
+ // Fix base version.
+ _delta.baseVersion = document.version;
- if (a.end.isTouching(b.start)) {
- a.end = b.end;
- transformed.splice(i, 1);
- i--;
- }
- }
+ // Before applying, add the delta to the `undoingBatch`.
+ undoingBatch.addDelta(_delta);
- return transformed;
-}
+ // Now, apply all operations of the delta.
+ var _iteratorNormalCompletion6 = true;
+ var _didIteratorError6 = false;
+ var _iteratorError6 = undefined;
-// Transforms given set of `ranges` by given set of `deltas`. Returns transformed `ranges`.
-function transformRangesByDeltas(ranges, deltas) {
- var _iteratorNormalCompletion4 = true;
- var _didIteratorError4 = false;
- var _iteratorError4 = undefined;
+ try {
+ for (var _iterator6 = _delta.operations[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
+ var operation = _step6.value;
- try {
- for (var _iterator4 = deltas[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
- var delta = _step4.value;
- var _iteratorNormalCompletion5 = true;
- var _didIteratorError5 = false;
- var _iteratorError5 = undefined;
+ document.applyOperation(operation);
+ }
+ } catch (err) {
+ _didIteratorError6 = true;
+ _iteratorError6 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion6 && _iterator6.return) {
+ _iterator6.return();
+ }
+ } finally {
+ if (_didIteratorError6) {
+ throw _iteratorError6;
+ }
+ }
+ }
+ }
- try {
- for (var _iterator5 = delta.operations[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
- var operation = _step5.value;
+ // 5. Remove reversed delta from the history.
+ } catch (err) {
+ _didIteratorError4 = true;
+ _iteratorError4 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion4 && _iterator4.return) {
+ _iterator4.return();
+ }
+ } finally {
+ if (_didIteratorError4) {
+ throw _iteratorError4;
+ }
+ }
+ }
- // We look through all operations from all deltas.
+ history.removeDelta(baseVersion);
- for (var i = 0; i < ranges.length; i++) {
- // We transform every range by every operation.
- var result = void 0;
+ // And all deltas that are reversing it.
+ // So the history looks like both original and reversing deltas never happened.
+ // That's why we have to update history deltas - some of them might have been basing on deltas that we are now removing.
+ var _iteratorNormalCompletion5 = true;
+ var _didIteratorError5 = false;
+ var _iteratorError5 = undefined;
- switch (operation.type) {
- case 'insert':
- result = ranges[i]._getTransformedByInsertion(operation.position, operation.nodes.maxOffset, true);
- break;
+ try {
+ for (var _iterator5 = reversedDelta[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
+ var _delta2 = _step5.value;
- case 'move':
- case 'remove':
- case 'reinsert':
- result = ranges[i]._getTransformedByMove(operation.sourcePosition, operation.targetPosition, operation.howMany, true);
- break;
+ history.removeDelta(_delta2.baseVersion);
}
- // If we have a transformation result, we substitute transformed range with it in `transformed` array.
- // Keep in mind that the result is an array and may contain multiple ranges.
- if (result) {
- ranges.splice.apply(ranges, [i, 1].concat(toConsumableArray(result)));
-
- // Fix iterator.
- i = i + result.length - 1;
+ // 6. Update history deltas in history.
+ } catch (err) {
+ _didIteratorError5 = true;
+ _iteratorError5 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion5 && _iterator5.return) {
+ _iterator5.return();
+ }
+ } finally {
+ if (_didIteratorError5) {
+ throw _iteratorError5;
+ }
}
}
+
+ for (var historyBaseVersion in updatedHistoryDeltas) {
+ history.updateDelta(Number(historyBaseVersion), updatedHistoryDeltas[historyBaseVersion]);
+ }
}
} catch (err) {
- _didIteratorError5 = true;
- _iteratorError5 = err;
+ _didIteratorError = true;
+ _iteratorError = err;
} finally {
try {
- if (!_iteratorNormalCompletion5 && _iterator5.return) {
- _iterator5.return();
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
}
} finally {
- if (_didIteratorError5) {
- throw _iteratorError5;
+ if (_didIteratorError) {
+ throw _iteratorError;
}
}
}
- }
- } catch (err) {
- _didIteratorError4 = true;
- _iteratorError4 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion4 && _iterator4.return) {
- _iterator4.return();
- }
- } finally {
- if (_didIteratorError4) {
- throw _iteratorError4;
- }
- }
- }
- return ranges;
-}
+ return undoingBatch;
+ }
+ }]);
+ return UndoCommand;
+}(BaseCommand);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -51131,148 +50972,117 @@ function transformRangesByDeltas(ranges, deltas) {
*/
/**
- * The undo command stores {@link engine.model.Batch batches} applied to the {@link engine.model.Document document}
- * and is able to undo a batch by reversing it and transforming by other batches from {@link engine.model.Document#history history}
- * that happened after the reversed batch.
+ * The redo command stores {@link engine.model.Batch batches} that were used to undo a batch by {@link undo.UndoCommand UndoCommand}.
+ * It is able to redo a previously undone batch by reversing the undoing batches created by `UndoCommand`. The reversed batch is
+ * also transformed by batches from {@link engine.model.Document#history history} that happened after it and are not other redo batches.
*
- * The undo command also takes care of restoring the {@link engine.model.Document#selection document selection} to the state before
- * the undone batch was applied.
+ * The redo command also takes care of restoring the {@link engine.model.Document#selection document selection} to the state before
+ * an undone batch was applied.
*
* @memberOf undo
* @extends undo.BaseCommand
*/
-var UndoCommand = function (_BaseCommand) {
- inherits(UndoCommand, _BaseCommand);
+var RedoCommand = function (_BaseCommand) {
+ inherits(RedoCommand, _BaseCommand);
- function UndoCommand() {
- classCallCheck(this, UndoCommand);
- return possibleConstructorReturn(this, Object.getPrototypeOf(UndoCommand).apply(this, arguments));
+ function RedoCommand() {
+ classCallCheck(this, RedoCommand);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(RedoCommand).apply(this, arguments));
}
- createClass(UndoCommand, [{
+ createClass(RedoCommand, [{
key: '_doExecute',
/**
- * Executes the command. This method reverts a {@link engine.model.Batch batch} added to the command's stack, transforms
- * and applies the reverted version on the {@link engine.model.Document document} and removes the batch from the stack.
+ * Executes the command. This method reverts the last {@link engine.model.Batch batch} added to the command's stack, applies
+ * the reverted and transformed version on the {@link engine.model.Document document} and removes the batch from the stack.
* Then, it restores the {@link engine.model.Document#selection document selection}.
*
* @protected
- * @fires undo.UndoCommand#event:revert
- * @param {engine.model.Batch} [batch] A batch that should be undone. If not set, the last added batch will be undone.
*/
value: function _doExecute() {
var _this2 = this;
- var batch = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0];
-
- // If batch is not given, set `batchIndex` to the last index in command stack.
- var batchIndex = batch ? this._stack.findIndex(function (a) {
- return a.batch == batch;
- }) : this._stack.length - 1;
-
- var item = this._stack.splice(batchIndex, 1)[0];
+ var item = this._stack.pop();
- // All changes has to be done in one `enqueueChanges` callback so other listeners will not
+ // All changes have to be done in one `enqueueChanges` callback so other listeners will not
// step between consecutive deltas, or won't do changes to the document before selection is properly restored.
this.editor.document.enqueueChanges(function () {
- var undoingBatch = _this2._undo(item.batch);
+ var lastDelta = item.batch.deltas[item.batch.deltas.length - 1];
+ var nextBaseVersion = lastDelta.baseVersion + lastDelta.operations.length;
- var deltas = _this2.editor.document.history.getDeltas(item.batch.baseVersion);
- _this2._restoreSelection(item.selection.ranges, item.selection.isBackward, deltas);
+ // Selection state is from the moment after undo happened. It needs to be transformed by all the deltas
+ // that happened after the selection state got saved. Unfortunately it is tricky, because those deltas
+ // are already compressed in the history (they are removed).
+ // Because of that we will transform the selection only by non-redo deltas
+ var deltas = Array.from(_this2.editor.document.history.getDeltas(nextBaseVersion)).filter(function (delta) {
+ return !_this2._createdBatches.has(delta.batch);
+ });
- _this2.fire('revert', item.batch, undoingBatch);
+ _this2._restoreSelection(item.selection.ranges, item.selection.isBackward, deltas);
+ _this2._redo(item.batch);
});
this.refreshState();
}
/**
- * Returns an index in {@link undo.BaseCommand#_stack} pointing to the item that is storing a batch that has a given
- * {@link engine.model.Batch#baseVersion}.
- *
- * @private
- * @param {Number} baseVersion The base version of the batch to find.
- * @returns {Number|null}
- */
-
- }, {
- key: '_getItemIndexFromBaseVersion',
- value: function _getItemIndexFromBaseVersion(baseVersion) {
- for (var i = 0; i < this._stack.length; i++) {
- if (this._stack[i].batch.baseVersion == baseVersion) {
- return i;
- }
- }
-
- return null;
- }
-
- /**
- * Undoes a batch by reversing a batch from history, transforming that reversed batch and applying it. This is
- * a helper method for {@link undo.UndoCommand#_doExecute}.
+ * Redoes a batch by reversing the batch that has undone it, transforming that batch and applying it. This is
+ * a helper method for {@link undo.RedoCommand#_doExecute}.
*
* @private
- * @param {engine.model.Batch} batchToUndo A batch whose deltas will be reversed, transformed and applied.
+ * @param {engine.model.Batch} storedBatch The batch whose deltas will be reversed, transformed and applied.
*/
}, {
- key: '_undo',
- value: function _undo(batchToUndo) {
+ key: '_redo',
+ value: function _redo(storedBatch) {
var document = this.editor.document;
// All changes done by the command execution will be saved as one batch.
- var undoingBatch = document.batch();
- this._createdBatches.add(undoingBatch);
+ var redoingBatch = document.batch();
+ this._createdBatches.add(redoingBatch);
- var history = document.history;
- var deltasToUndo = batchToUndo.deltas.slice();
- deltasToUndo.reverse();
+ var deltasToRedo = storedBatch.deltas.slice();
+ deltasToRedo.reverse();
- // We will process each delta from `batchToUndo`, in reverse order. If there was deltas A, B and C in undone batch,
+ // We will process each delta from `storedBatch`, in reverse order. If there was deltas A, B and C in stored batch,
// we need to revert them in reverse order, so first reverse C, then B, then A.
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
- for (var _iterator = deltasToUndo[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var deltaToUndo = _step.value;
+ for (var _iterator = deltasToRedo[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var deltaToRedo = _step.value;
// Keep in mind that all algorithms return arrays. That's because the transformation might result in multiple
// deltas, so we need arrays to handle them anyway. To simplify algorithms, it is better to always have arrays
// in mind. For simplicity reasons, we will use singular form in descriptions and names.
- var baseVersion = deltaToUndo.baseVersion;
- var nextBaseVersion = baseVersion + deltaToUndo.operations.length;
-
- // 1. Get updated version of the delta from the history.
- // Batch stored in the undo command might have an outdated version of the delta that should be undone.
- // To prevent errors, we will take an updated version of it from the history, basing on delta's `baseVersion`.
- var updatedDeltaToUndo = history.getDelta(baseVersion);
- // This is a safe valve in case of not finding delta to undo in history. This may come up if that delta
- // got updated into no deltas, or removed from history.
- if (updatedDeltaToUndo === null) {
- continue;
- }
+ var nextBaseVersion = deltaToRedo.baseVersion + deltaToRedo.operations.length;
- // 2. Reverse delta from the history.
- updatedDeltaToUndo.reverse();
- var reversedDelta = [];
+ // As stated above, convert delta to array of deltas.
+ var reversedDelta = [deltaToRedo.getReversed()];
+ // 1. Transform that delta by deltas from history that happened after it.
+ // Omit deltas from "redo" batches, because reversed delta already bases on them. Transforming by them
+ // again will result in incorrect deltas.
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
- for (var _iterator2 = updatedDeltaToUndo[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- var delta = _step2.value;
+ for (var _iterator2 = document.history.getDeltas(nextBaseVersion)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var historyDelta = _step2.value;
- reversedDelta.push(delta.getReversed());
+ if (!this._createdBatches.has(historyDelta.batch)) {
+ reversedDelta = transformDelta(reversedDelta, [historyDelta], true);
+ }
}
- // Stores history deltas transformed by `deltaToUndo`. Will be used later for updating document history.
+ // 2. After reversed delta has been transformed by all history deltas, apply it.
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
@@ -51288,46 +51098,46 @@ var UndoCommand = function (_BaseCommand) {
}
}
- var updatedHistoryDeltas = {};
-
- // 3. Transform reversed delta by history deltas that happened after delta to undo. We have to bring
- // reversed delta to the current state of document. While doing this, we will also update history deltas
- // to the state which "does not remember" delta that we undo.
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
- for (var _iterator3 = history.getDeltas(nextBaseVersion)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
- var historyDelta = _step3.value;
+ for (var _iterator3 = reversedDelta[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
+ var delta = _step3.value;
- // 3.1. Transform selection range stored with history batch by reversed delta.
- // It is important to keep stored selection ranges updated. As we are removing and updating deltas in the history,
- // selection ranges would base on outdated history state.
- var itemIndex = this._getItemIndexFromBaseVersion(historyDelta.baseVersion);
+ // Fix base version.
+ delta.baseVersion = document.version;
- // `itemIndex` will be `null` for `historyDelta` if it is not the first delta in it's batch.
- // This is fine, because we want to transform each selection only once, before transforming reversed delta
- // by the first delta of the batch connected with the ranges.
- if (itemIndex !== null) {
- this._stack[itemIndex].selection.ranges = transformRangesByDeltas(this._stack[itemIndex].selection.ranges, reversedDelta);
- }
+ // Before applying, add the delta to the `redoingBatch`.
+ redoingBatch.addDelta(delta);
- // 3.2. Transform history delta by reversed delta. We need this to update document history.
- var updatedHistoryDelta = transformDelta([historyDelta], reversedDelta, false);
+ // Now, apply all operations of the delta.
+ var _iteratorNormalCompletion4 = true;
+ var _didIteratorError4 = false;
+ var _iteratorError4 = undefined;
- // 3.3. Transform reversed delta by history delta (in state before transformation above).
- reversedDelta = transformDelta(reversedDelta, [historyDelta], true);
+ try {
+ for (var _iterator4 = delta.operations[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
+ var operation = _step4.value;
- // 3.4. Store updated history delta. Later, it will be updated in `history`.
- if (!updatedHistoryDeltas[historyDelta.baseVersion]) {
- updatedHistoryDeltas[historyDelta.baseVersion] = [];
+ document.applyOperation(operation);
+ }
+ } catch (err) {
+ _didIteratorError4 = true;
+ _iteratorError4 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion4 && _iterator4.return) {
+ _iterator4.return();
+ }
+ } finally {
+ if (_didIteratorError4) {
+ throw _iteratorError4;
+ }
+ }
}
-
- updatedHistoryDeltas[historyDelta.baseVersion] = updatedHistoryDeltas[historyDelta.baseVersion].concat(updatedHistoryDelta);
}
-
- // 4. After reversed delta has been transformed by all history deltas, apply it.
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
@@ -51342,120 +51152,312 @@ var UndoCommand = function (_BaseCommand) {
}
}
}
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+ }
+ }]);
+ return RedoCommand;
+}(BaseCommand);
+
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * The undo engine feature.
+ *
+ * Undo brings in possibility to undo and redo changes done in the model by deltas through
+ * the {@link engine.model.Document#batch Batch API}.
+ *
+ * @memberOf undo
+ * @extends core.Feature
+ */
+
+var UndoEngine = function (_Feature) {
+ inherits(UndoEngine, _Feature);
+
+ /**
+ * @inheritDoc
+ */
+ function UndoEngine(editor) {
+ classCallCheck(this, UndoEngine);
+
+ /**
+ * The command that manages undo {@link engine.model.Batch batches} stack (history).
+ * Created and registered during the {@link undo.UndoEngine#init feature initialization}.
+ *
+ * @private
+ * @member {undo.UndoEngineCommand} undo.UndoEngine#_undoCommand
+ */
+
+ /**
+ * The command that manages redo {@link engine.model.Batch batches} stack (history).
+ * Created and registered during the {@link undo.UndoEngine#init feature initialization}.
+ *
+ * @private
+ * @member {undo.UndoEngineCommand} undo.UndoEngine#_redoCommand
+ */
+
+ /**
+ * Keeps track of which batches were registered in undo.
+ *
+ * @private
+ * @member {WeakSet.} undo.UndoEngine#_batchRegistry
+ */
+ var _this = possibleConstructorReturn(this, Object.getPrototypeOf(UndoEngine).call(this, editor));
+
+ _this._batchRegistry = new WeakSet();
+ return _this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+
+
+ createClass(UndoEngine, [{
+ key: 'init',
+ value: function init() {
+ var _this2 = this;
+
+ // Create commands.
+ this._undoCommand = new UndoCommand(this.editor);
+ this._redoCommand = new RedoCommand(this.editor);
+
+ // Register command to the editor.
+ this.editor.commands.set('undo', this._undoCommand);
+ this.editor.commands.set('redo', this._redoCommand);
+
+ this.listenTo(this.editor.document, 'change', function (evt, type, changes, batch) {
+ // If changes are not a part of a batch or this is not a new batch, omit those changes.
+ if (_this2._batchRegistry.has(batch) || batch.type == 'transparent') {
+ return;
+ } else {
+ if (_this2._redoCommand._createdBatches.has(batch)) {
+ // If this batch comes from `redoCommand`, add it to `undoCommand` stack.
+ _this2._undoCommand.addBatch(batch);
+ } else if (!_this2._undoCommand._createdBatches.has(batch)) {
+ // A default batch - these are new changes in the document, not introduced by undo feature.
+ // Add them to `undoCommand` stack and clear `redoCommand` stack.
+ _this2._undoCommand.addBatch(batch);
+ _this2._redoCommand.clearStack();
+ }
+ }
+
+ // Add the batch to the registry so it will not be processed again.
+ _this2._batchRegistry.add(batch);
+ }, { priority: 'highest' });
+
+ this.listenTo(this._undoCommand, 'revert', function (evt, undoneBatch, undoingBatch) {
+ _this2._redoCommand.addBatch(undoingBatch);
+ });
+ }
+ }]);
+ return UndoEngine;
+}(Feature);
+
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * The undo feature. It introduces the Undo and Redo buttons to the editor.
+ *
+ * Below is the explanation of the undo mechanism working together with {@link engine.model.History History}:
+ *
+ * Whenever a {@link engine.model.Delta delta} is applied to the {@link engine.model.Document document}, it is saved to
+ * `History` as is. The {@link engine.model.Batch batch} that owns that delta is also saved, in {@link undo.UndoCommand},
+ * together with the selection that was present in the document before the delta was applied. A batch is saved instead of the delta
+ * because changes are undone batch-by-batch, not delta-by-delta and a batch is seen as one undo step.
+ *
+ * After some changes happen to the document, the `History` and `UndoCommand` stack can be represented as follows:
+ *
+ * History Undo stack
+ * =========== ==================================
+ * [delta A1] [batch A with selection before A1]
+ * [delta B1] [batch B with selection before B1]
+ * [delta B2] [batch C with selection before C1]
+ * [delta C1]
+ * [delta C2]
+ * [delta B3]
+ * [delta C3]
+ *
+ * Where deltas starting with the same letter are from same batch.
+ *
+ * Undoing a batch means that a set of deltas which will reverse the effects of that batch needs to be generated. For example, if a batch
+ * added several letters, undoing the batch should remove them. It is important to apply undoing deltas in the reversed order,
+ * so if a batch has delta `X`, `Y`, `Z`, reversed deltas `Zr`, `Yr` and `Xr` need to be applied. Otherwise reversed delta
+ * `Xr` would operate on a wrong document state, because delta `X` does not know that deltas `Y` and `Z` happened.
+ *
+ * After deltas from an undone batch got {@link engine.model.Delta#getReversed reversed}, one needs to make sure if they are
+ * ready to be applied. In the scenario above, delta `C3` is the last delta and `C3r` bases on up-to-date document state, so
+ * it can be applied to the document.
+ *
+ * History Undo stack
+ * =========== ==================================
+ * [delta A1 ] [batch A with selection before A1]
+ * [delta B1 ] [batch B with selection before B1]
+ * [delta B2 ] [ processing undoing batch C ]
+ * [delta C1 ]
+ * [delta C2 ]
+ * [delta B3 ]
+ * [delta C3 ]
+ * [delta C3r]
+ *
+ * Next is delta `C2`, reversed to `C2r`. `C2r` bases on `C2`, so it bases on the wrong document state. It needs to be
+ * transformed by deltas from history that happened after it, so it "knows" about them. Let us assume that `C2' = C2r * B3 * C3 * C3r`,
+ * where `*` means "transformed by". As can be seen, `C2r` is transformed by a delta which is undone afterwards anyway.
+ * This brings two problems: lower effectiveness (obvious) and incorrect results. Bad results come from the fact that
+ * operational transformation algorithms assume there is no connection between two transformed operations when resolving
+ * conflicts, which is true for example for collaborative editing, but is not true for the undo algorithm.
+ *
+ * To prevent both problems, `History` introduces an API to {@link engine.model.History#removeDelta remove}
+ * deltas from history. It is used to remove undone and undoing deltas after they are applied. It feels right — since when a
+ * delta is undone or reversed, it is "removed" and there should be no sign of it in the history (fig. 1).
+ *
+ * Notes:
+ *
+ * * `---` symbolizes a removed delta.
+ * * `'` symbolizes a reversed delta that was later transformed.
+ *
+ * History (fig. 1) History (fig. 2) History (fig. 3)
+ * ================ ================ ================
+ * [delta A1] [delta A1] [delta A1]
+ * [delta B1] [delta B1] [delta B1]
+ * [delta B2] [delta B2] [delta B2]
+ * [delta C1] [delta C1] [---C1---]
+ * [delta C2] [---C2---] [---C2---]
+ * [delta B3] [delta B3] [delta B3]
+ * [---C3---] [---C3---] [---C3---]
+ * [---C3r--] [---C3r--] [---C3r--]
+ * [---C2'--] [---C2'--]
+ * [---C1'--]
+ *
+ * `C2r` can now be transformed only by `B3` and both `C2'` and `C2` can be removed (fig. 2). Same with `C1` (fig. 3).
+ *
+ * But what about that selection? For batch `C`, undo feature remembers the selection just before `C1` was applied. It can be
+ * visualized between delta `B2` and `B3` (see fig. 3). As can be seen, some operations were applied to the document since the selection
+ * state was remembered. Setting the document selection as it was remembered would be incorrect. It feels natural that
+ * the selection state should also be transformed by deltas from history. The same pattern applies as with transforming deltas —
+ * ranges should not be transformed by undone and undoing deltas. Thankfully, those deltas are already removed from history.
+ *
+ * Unfortunately, a problem appears with delta `B3`. It still remembers the context of deltas `C2` and `C1` on which it bases.
+ * It is an obvious error — transforming by that delta would lead to incorrect results or "repeating" history would
+ * produce a different document than the actual one.
+ *
+ * To prevent this situation, `B3` needs to also be {@link engine.model.History#updateDelta updated} in history.
+ * It should be kept in a state that "does not remember" deltas that were removed from history. It is easily
+ * achieved while transforming the reversed delta. For example, when `C2r` is transformed by `B3`, at the same time `B3` is
+ * transformed by `C2r`. Transforming `B3` that remembers `C2` by a delta reversing `C2` effectively makes `B3` "forget" about `C2`.
+ * By doing these transformations you effectively make `B3` base on `B2` which is the correct state of history (fig. 4).
+ *
+ * History (fig. 4) History (fig. 5)
+ * =========================== ===============================
+ * [delta A1] [---A1---]
+ * [delta B1] [delta B1 "without A1"]
+ * [delta B2] [delta B2 "without A1"]
+ * [---C1---] [---C1---]
+ * [---C2---] [---C2---]
+ * [delta B3 "without C2, C1"] [delta B3 "without C2, C1, A1"]
+ * [---C3---] [---C3---]
+ * [---C3r--] [---C3r--]
+ * [---C2'--] [---C2'--]
+ * [---C1'--] [---C1'--]
+ * [---A1'--]
+ *
+ * Selective undo works on the same basis, however, instead of undoing the last batch in the undo stack, any batch can be undone.
+ * The same algorithm applies: deltas from a batch (i.e. `A1`) are reversed and then transformed by deltas stored in history,
+ * simultaneously updating them. Then deltas are applied to the document and removed from history (fig. 5).
+ *
+ * @memberOf undo
+ * @extends core.Feature
+ */
- var _iteratorNormalCompletion4 = true;
- var _didIteratorError4 = false;
- var _iteratorError4 = undefined;
+var Undo = function (_Feature) {
+ inherits(Undo, _Feature);
- try {
- for (var _iterator4 = reversedDelta[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
- var _delta = _step4.value;
+ function Undo() {
+ classCallCheck(this, Undo);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Undo).apply(this, arguments));
+ }
- // Fix base version.
- _delta.baseVersion = document.version;
+ createClass(Undo, [{
+ key: 'init',
- // Before applying, add the delta to the `undoingBatch`.
- undoingBatch.addDelta(_delta);
- // Now, apply all operations of the delta.
- var _iteratorNormalCompletion6 = true;
- var _didIteratorError6 = false;
- var _iteratorError6 = undefined;
+ /**
+ * @inheritDoc
+ */
+ value: function init() {
+ var editor = this.editor;
+ var t = editor.t;
- try {
- for (var _iterator6 = _delta.operations[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
- var operation = _step6.value;
+ this._addButton('undo', t('Undo'), 'CTRL+Z');
+ this._addButton('redo', t('Redo'), 'CTRL+Y');
- document.applyOperation(operation);
- }
- } catch (err) {
- _didIteratorError6 = true;
- _iteratorError6 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion6 && _iterator6.return) {
- _iterator6.return();
- }
- } finally {
- if (_didIteratorError6) {
- throw _iteratorError6;
- }
- }
- }
- }
+ editor.keystrokes.set('CTRL+Z', 'undo');
+ editor.keystrokes.set('CTRL+Y', 'redo');
+ editor.keystrokes.set('CTRL+SHIFT+Z', 'redo');
+ }
- // 5. Remove reversed delta from the history.
- } catch (err) {
- _didIteratorError4 = true;
- _iteratorError4 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion4 && _iterator4.return) {
- _iterator4.return();
- }
- } finally {
- if (_didIteratorError4) {
- throw _iteratorError4;
- }
- }
- }
+ /**
+ * Creates a button for the specified command.
+ *
+ * @private
+ * @param {String} name Command name.
+ * @param {String} label Button label.
+ * @param {String} keystroke Command keystroke.
+ */
- history.removeDelta(baseVersion);
+ }, {
+ key: '_addButton',
+ value: function _addButton(name, label, keystroke) {
+ var editor = this.editor;
- // And all deltas that are reversing it.
- // So the history looks like both original and reversing deltas never happened.
- // That's why we have to update history deltas - some of them might have been basing on deltas that we are now removing.
- var _iteratorNormalCompletion5 = true;
- var _didIteratorError5 = false;
- var _iteratorError5 = undefined;
+ var command = editor.commands.get(name);
- try {
- for (var _iterator5 = reversedDelta[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
- var _delta2 = _step5.value;
+ var model = new Model({
+ isOn: false,
+ label: label,
+ icon: name,
+ keystroke: keystroke
+ });
- history.removeDelta(_delta2.baseVersion);
- }
+ model.bind('isEnabled').to(command, 'isEnabled');
- // 6. Update history deltas in history.
- } catch (err) {
- _didIteratorError5 = true;
- _iteratorError5 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion5 && _iterator5.return) {
- _iterator5.return();
- }
- } finally {
- if (_didIteratorError5) {
- throw _iteratorError5;
- }
- }
- }
+ this.listenTo(model, 'execute', function () {
+ return editor.execute(name);
+ });
- for (var historyBaseVersion in updatedHistoryDeltas) {
- history.updateDelta(Number(historyBaseVersion), updatedHistoryDeltas[historyBaseVersion]);
- }
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
+ editor.ui.featureComponents.add(name, Button, ButtonView, model);
+ }
+ }], [{
+ key: 'requires',
- return undoingBatch;
- }
- }]);
- return UndoCommand;
-}(BaseCommand);
+ /**
+ * @inheritDoc
+ */
+ get: function get() {
+ return [UndoEngine];
+ }
+ }]);
+ return Undo;
+}(Feature);
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -51463,303 +51465,350 @@ var UndoCommand = function (_BaseCommand) {
*/
/**
- * The redo command stores {@link engine.model.Batch batches} that were used to undo a batch by {@link undo.UndoCommand UndoCommand}.
- * It is able to redo a previously undone batch by reversing the undoing batches created by `UndoCommand`. The reversed batch is
- * also transformed by batches from {@link engine.model.Document#history history} that happened after it and are not other redo batches.
+ * The block autoformatting engine. Allows to format various block patterns. For example,
+ * it can be configured to make a paragraph starting with "* " a list item.
+ *
+ * The autoformatting operation is integrated with the undo manager,
+ * so the autoformatting step can be undone, if the user's intention wasn't to format the text.
+ *
+ * See the constructors documentation to learn how to create custom inline autoformatters. You can also use
+ * the {@link autoformat.Autoformat} feature which enables a set of default autoformatters (lists, headings, bold and italic).
+ *
+ * @memberOf autoformat
+ */
+
+var BlockAutoformatEngine =
+/**
+ * Creates listener triggered on `change` event in document.
+ * Calls callback when inserted text matches regular expression or command name
+ * if provided instead of callback.
*
- * The redo command also takes care of restoring the {@link engine.model.Document#selection document selection} to the state before
- * an undone batch was applied.
+ * Examples of usage:
*
- * @memberOf undo
- * @extends undo.BaseCommand
+ * To convert paragraph to heading1 when `- ` is typed, using just commmand name:
+ *
+ * new BlockAutoformatEngine( editor, /^\- $/, 'heading1' );
+ *
+ * To convert paragraph to heading1 when `- ` is typed, using just callback:
+ *
+ * new BlockAutoformatEngine( editor, /^\- $/, ( context ) => {
+ * const { batch, match } = context;
+ * const headingLevel = match[ 1 ].length;
+ *
+ * editor.execute( 'heading', {
+ * batch,
+ * formatId: `heading${ headingLevel }`
+ * } );
+ * } );
+ *
+ * @param {core.editor.Editor} editor Editor instance.
+ * @param {RegExp} pattern Regular expression to exec on just inserted text.
+ * @param {Function|String} callbackOrCommand Callback to execute or command to run when text is matched.
+ * In case of providing callback it receives following parameters:
+ * * {engine.model.Batch} batch Newly created batch for autoformat changes.
+ * * {Object} match RegExp.exec() result of matching pattern to inserted text.
*/
+function BlockAutoformatEngine(editor, pattern, callbackOrCommand) {
+ classCallCheck(this, BlockAutoformatEngine);
-var RedoCommand = function (_BaseCommand) {
- inherits(RedoCommand, _BaseCommand);
+ var callback = void 0;
- function RedoCommand() {
- classCallCheck(this, RedoCommand);
- return possibleConstructorReturn(this, Object.getPrototypeOf(RedoCommand).apply(this, arguments));
+ if (typeof callbackOrCommand == 'function') {
+ callback = callbackOrCommand;
+ } else {
+ (function () {
+ // We assume that the actual command name was provided.
+ var command = callbackOrCommand;
+
+ callback = function callback(context) {
+ var batch = context.batch;
+
+ // Create new batch for removal and command execution.
+
+ editor.execute(command, { batch: batch });
+ };
+ })();
}
- createClass(RedoCommand, [{
- key: '_doExecute',
+ editor.document.on('change', function (event, type, changes) {
+ if (type != 'insert') {
+ return;
+ }
- /**
- * Executes the command. This method reverts the last {@link engine.model.Batch batch} added to the command's stack, applies
- * the reverted and transformed version on the {@link engine.model.Document document} and removes the batch from the stack.
- * Then, it restores the {@link engine.model.Document#selection document selection}.
- *
- * @protected
- */
- value: function _doExecute() {
- var _this2 = this;
+ // Take the first element. Typing shouldn't add more than one element at once.
+ // And if it is not typing (e.g. paste), Autoformat should not be fired.
+ var value = changes.range.getItems().next().value;
- var item = this._stack.pop();
+ if (!(value instanceof TextProxy)) {
+ return;
+ }
- // All changes have to be done in one `enqueueChanges` callback so other listeners will not
- // step between consecutive deltas, or won't do changes to the document before selection is properly restored.
- this.editor.document.enqueueChanges(function () {
- var lastDelta = item.batch.deltas[item.batch.deltas.length - 1];
- var nextBaseVersion = lastDelta.baseVersion + lastDelta.operations.length;
+ var textNode = value.textNode;
+ var text = textNode.data;
- // Selection state is from the moment after undo happened. It needs to be transformed by all the deltas
- // that happened after the selection state got saved. Unfortunately it is tricky, because those deltas
- // are already compressed in the history (they are removed).
- // Because of that we will transform the selection only by non-redo deltas
- var deltas = Array.from(_this2.editor.document.history.getDeltas(nextBaseVersion)).filter(function (delta) {
- return !_this2._createdBatches.has(delta.batch);
- });
+ // Run matching only on non-empty paragraphs.
+ if (textNode.parent.name !== 'paragraph' || !text) {
+ return;
+ }
- _this2._restoreSelection(item.selection.ranges, item.selection.isBackward, deltas);
- _this2._redo(item.batch);
- });
+ var match = pattern.exec(text);
- this.refreshState();
+ if (!match) {
+ return;
}
- /**
- * Redoes a batch by reversing the batch that has undone it, transforming that batch and applying it. This is
- * a helper method for {@link undo.RedoCommand#_doExecute}.
- *
- * @private
- * @param {engine.model.Batch} storedBatch The batch whose deltas will be reversed, transformed and applied.
- */
+ editor.document.enqueueChanges(function () {
+ // Create new batch to separate typing batch from the Autoformat changes.
+ var batch = editor.document.batch();
- }, {
- key: '_redo',
- value: function _redo(storedBatch) {
- var document = this.editor.document;
+ // Matched range.
+ var range = Range$1.createFromParentsAndOffsets(textNode.parent, 0, textNode.parent, match[0].length);
- // All changes done by the command execution will be saved as one batch.
- var redoingBatch = document.batch();
- this._createdBatches.add(redoingBatch);
+ // Remove matched text.
+ batch.remove(range);
- var deltasToRedo = storedBatch.deltas.slice();
- deltasToRedo.reverse();
+ callback({ batch: batch, match: match });
+ });
+ });
+};
- // We will process each delta from `storedBatch`, in reverse order. If there was deltas A, B and C in stored batch,
- // we need to revert them in reverse order, so first reverse C, then B, then A.
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
- try {
- for (var _iterator = deltasToRedo[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var deltaToRedo = _step.value;
+/**
+ * The inline autoformatting engine. Allows to format various inline patterns. For example,
+ * it can be configured to make "foo" bold when typed `**foo**` (the `**` markers will be removed).
+ *
+ * The autoformatting operation is integrated with the undo manager,
+ * so the autoformatting step can be undone, if the user's intention wasn't to format the text.
+ *
+ * See the constructors documentation to learn how to create custom inline autoformatters. You can also use
+ * the {@link autoformat.Autoformat} feature which enables a set of default autoformatters (lists, headings, bold and italic).
+ *
+ * @memberOf autoformat
+ */
- // Keep in mind that all algorithms return arrays. That's because the transformation might result in multiple
- // deltas, so we need arrays to handle them anyway. To simplify algorithms, it is better to always have arrays
- // in mind. For simplicity reasons, we will use singular form in descriptions and names.
+var InlineAutoformatEngine =
+/**
+ * Enables autoformatting mechanism on a given {@link core.editor.Editor}.
+ *
+ * It formats the matched text by applying given model attribute or by running the provided formatting callback.
+ * Each time data model changes text from given node (from the beginning of the current node to the collapsed
+ * selection location) will be tested.
+ *
+ * @param {core.editor.Editor} editor Editor instance.
+ * @param {Function|RegExp} testRegexpOrCallback RegExp or callback to execute on text.
+ * Provided RegExp *must* have three capture groups. First and third capture groups
+ * should match opening/closing delimiters. Second capture group should match text to format.
+ *
+ * // Matches `**bold text**` pattern.
+ * // There are three capturing groups:
+ * // - first to match starting `**` delimiter,
+ * // - second to match text to format,
+ * // - third to match ending `**` delimiter.
+ * new InlineAutoformatEngine( this.editor, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' );
+ *
+ * When function is provided instead of RegExp, it will be executed with text to match as a parameter. Function
+ * should return proper "ranges" to delete and format.
+ *
+ * {
+ * remove: [
+ * [ 0, 1 ], // Remove first letter from the given text.
+ * [ 5, 6 ] // Remove 6th letter from the given text.
+ * ],
+ * format: [
+ * [ 1, 5 ] // Format all letters from 2nd to 5th.
+ * ]
+ * }
+ *
+ * @param {Function|String} attributeOrCallback Name of attribute to apply on matching text or callback for manual
+ * formatting.
+ *
+ * // Use attribute name:
+ * new InlineAutoformatEngine( this.editor, /(\*\*)([^\*]+?)(\*\*)$/g, 'bold' );
+ *
+ * // Use formatting callback:
+ * new InlineAutoformatEngine( this.editor, /(\*\*)([^\*]+?)(\*\*)$/g, ( batch, validRanges ) => {
+ * for ( let range of validRanges ) {
+ * batch.setAttribute( range, command, true );
+ * }
+ * } );
+ */
+function InlineAutoformatEngine(editor, testRegexpOrCallback, attributeOrCallback) {
+ var _this = this;
- var nextBaseVersion = deltaToRedo.baseVersion + deltaToRedo.operations.length;
+ classCallCheck(this, InlineAutoformatEngine);
- // As stated above, convert delta to array of deltas.
- var reversedDelta = [deltaToRedo.getReversed()];
+ this.editor = editor;
- // 1. Transform that delta by deltas from history that happened after it.
- // Omit deltas from "redo" batches, because reversed delta already bases on them. Transforming by them
- // again will result in incorrect deltas.
- var _iteratorNormalCompletion2 = true;
- var _didIteratorError2 = false;
- var _iteratorError2 = undefined;
+ var regExp = void 0;
+ var command = void 0;
+ var testCallback = void 0;
+ var formatCallback = void 0;
- try {
- for (var _iterator2 = document.history.getDeltas(nextBaseVersion)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- var historyDelta = _step2.value;
+ if (testRegexpOrCallback instanceof RegExp) {
+ regExp = testRegexpOrCallback;
+ } else {
+ testCallback = testRegexpOrCallback;
+ }
- if (!this._createdBatches.has(historyDelta.batch)) {
- reversedDelta = transformDelta(reversedDelta, [historyDelta], true);
- }
- }
+ if (typeof attributeOrCallback == 'string') {
+ command = attributeOrCallback;
+ } else {
+ formatCallback = attributeOrCallback;
+ }
+
+ // A test callback run on changed text.
+ testCallback = testCallback || function (text) {
+ var result = void 0;
+ var remove = [];
+ var format = [];
+
+ while ((result = regExp.exec(text)) !== null) {
+ // There should be full match and 3 capture groups.
+ if (result && result.length < 4) {
+ break;
+ }
+
+ var _result = result;
+ var index = _result.index;
+ var leftDel = _result['1'];
+ var content = _result['2'];
+ var rightDel = _result['3'];
+
+ // Real matched string - there might be some non-capturing groups so we need to recalculate starting index.
- // 2. After reversed delta has been transformed by all history deltas, apply it.
- } catch (err) {
- _didIteratorError2 = true;
- _iteratorError2 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion2 && _iterator2.return) {
- _iterator2.return();
- }
- } finally {
- if (_didIteratorError2) {
- throw _iteratorError2;
- }
- }
- }
+ var found = leftDel + content + rightDel;
+ index += result[0].length - found.length;
- var _iteratorNormalCompletion3 = true;
- var _didIteratorError3 = false;
- var _iteratorError3 = undefined;
+ // Start and End offsets of delimiters to remove.
+ var delStart = [index, index + leftDel.length];
+ var delEnd = [index + leftDel.length + content.length, index + leftDel.length + content.length + rightDel.length];
- try {
- for (var _iterator3 = reversedDelta[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
- var delta = _step3.value;
+ remove.push(delStart);
+ remove.push(delEnd);
- // Fix base version.
- delta.baseVersion = document.version;
+ format.push([index + leftDel.length, index + leftDel.length + content.length]);
+ }
- // Before applying, add the delta to the `redoingBatch`.
- redoingBatch.addDelta(delta);
+ return {
+ remove: remove,
+ format: format
+ };
+ };
- // Now, apply all operations of the delta.
- var _iteratorNormalCompletion4 = true;
- var _didIteratorError4 = false;
- var _iteratorError4 = undefined;
+ // A format callback run on matched text.
+ formatCallback = formatCallback || function (batch, validRanges) {
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
- try {
- for (var _iterator4 = delta.operations[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
- var operation = _step4.value;
+ try {
+ for (var _iterator = validRanges[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var range = _step.value;
- document.applyOperation(operation);
- }
- } catch (err) {
- _didIteratorError4 = true;
- _iteratorError4 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion4 && _iterator4.return) {
- _iterator4.return();
- }
- } finally {
- if (_didIteratorError4) {
- throw _iteratorError4;
- }
- }
- }
- }
- } catch (err) {
- _didIteratorError3 = true;
- _iteratorError3 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion3 && _iterator3.return) {
- _iterator3.return();
- }
- } finally {
- if (_didIteratorError3) {
- throw _iteratorError3;
- }
- }
- }
+ batch.setAttribute(range, command, true);
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return) {
+ _iterator.return();
}
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
} finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
+ if (_didIteratorError) {
+ throw _iteratorError;
}
}
}
- }]);
- return RedoCommand;
-}(BaseCommand);
+ };
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
+ editor.document.on('change', function (evt, type) {
+ if (type !== 'insert') {
+ return;
+ }
-/**
- * The undo engine feature.
- *
- * Undo brings in possibility to undo and redo changes done in the model by deltas through
- * the {@link engine.model.Document#batch Batch API}.
- *
- * @memberOf undo
- * @extends core.Feature
- */
+ var selection = _this.editor.document.selection;
-var UndoEngine = function (_Feature) {
- inherits(UndoEngine, _Feature);
+ if (!selection.isCollapsed || !selection.focus || !selection.focus.parent) {
+ return;
+ }
- /**
- * @inheritDoc
- */
- function UndoEngine(editor) {
- classCallCheck(this, UndoEngine);
+ var block = selection.focus.parent;
+ var text = getText(block).slice(0, selection.focus.offset + 1);
+ var ranges = testCallback(text);
+ var rangesToFormat = [];
- /**
- * The command that manages undo {@link engine.model.Batch batches} stack (history).
- * Created and registered during the {@link undo.UndoEngine#init feature initialization}.
- *
- * @private
- * @member {undo.UndoEngineCommand} undo.UndoEngine#_undoCommand
- */
+ // Apply format before deleting text.
+ ranges.format.forEach(function (range) {
+ if (range[0] === undefined || range[1] === undefined) {
+ return;
+ }
- /**
- * The command that manages redo {@link engine.model.Batch batches} stack (history).
- * Created and registered during the {@link undo.UndoEngine#init feature initialization}.
- *
- * @private
- * @member {undo.UndoEngineCommand} undo.UndoEngine#_redoCommand
- */
+ rangesToFormat.push(LiveRange.createFromParentsAndOffsets(block, range[0], block, range[1]));
+ });
- /**
- * Keeps track of which batches were registered in undo.
- *
- * @private
- * @member {WeakSet.} undo.UndoEngine#_batchRegistry
- */
- var _this = possibleConstructorReturn(this, Object.getPrototypeOf(UndoEngine).call(this, editor));
+ var rangesToRemove = [];
- _this._batchRegistry = new WeakSet();
- return _this;
- }
+ // Reverse order to not mix the offsets while removing.
+ ranges.remove.slice().reverse().forEach(function (range) {
+ if (range[0] === undefined || range[1] === undefined) {
+ return;
+ }
- /**
- * @inheritDoc
- */
+ rangesToRemove.push(LiveRange.createFromParentsAndOffsets(block, range[0], block, range[1]));
+ });
+ if (!(rangesToFormat.length && rangesToRemove.length)) {
+ return;
+ }
- createClass(UndoEngine, [{
- key: 'init',
- value: function init() {
- var _this2 = this;
+ var batch = editor.document.batch();
- // Create commands.
- this._undoCommand = new UndoCommand(this.editor);
- this._redoCommand = new RedoCommand(this.editor);
+ editor.document.enqueueChanges(function () {
+ var validRanges = getSchemaValidRanges(command, rangesToFormat, editor.document.schema);
- // Register command to the editor.
- this.editor.commands.set('undo', this._undoCommand);
- this.editor.commands.set('redo', this._redoCommand);
+ // Apply format.
+ formatCallback(batch, validRanges);
- this.listenTo(this.editor.document, 'change', function (evt, type, changes, batch) {
- // If changes are not a part of a batch or this is not a new batch, omit those changes.
- if (_this2._batchRegistry.has(batch) || batch.type == 'transparent') {
- return;
- } else {
- if (_this2._redoCommand._createdBatches.has(batch)) {
- // If this batch comes from `redoCommand`, add it to `undoCommand` stack.
- _this2._undoCommand.addBatch(batch);
- } else if (!_this2._undoCommand._createdBatches.has(batch)) {
- // A default batch - these are new changes in the document, not introduced by undo feature.
- // Add them to `undoCommand` stack and clear `redoCommand` stack.
- _this2._undoCommand.addBatch(batch);
- _this2._redoCommand.clearStack();
+ // Remove delimiters.
+ var _iteratorNormalCompletion2 = true;
+ var _didIteratorError2 = false;
+ var _iteratorError2 = undefined;
+
+ try {
+ for (var _iterator2 = rangesToRemove[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
+ var range = _step2.value;
+
+ batch.remove(range);
+ }
+ } catch (err) {
+ _didIteratorError2 = true;
+ _iteratorError2 = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion2 && _iterator2.return) {
+ _iterator2.return();
+ }
+ } finally {
+ if (_didIteratorError2) {
+ throw _iteratorError2;
}
}
+ }
+ });
+ });
+};
- // Add the batch to the registry so it will not be processed again.
- _this2._batchRegistry.add(batch);
- }, { priority: 'highest' });
-
- this.listenTo(this._undoCommand, 'revert', function (evt, undoneBatch, undoingBatch) {
- _this2._redoCommand.addBatch(undoingBatch);
- });
- }
- }]);
- return UndoEngine;
-}(Feature);
+function getText(element) {
+ return Array.from(element.getChildren()).reduce(function (a, b) {
+ return a + b.data;
+ }, '');
+}
/**
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
@@ -51767,187 +51816,138 @@ var UndoEngine = function (_Feature) {
*/
/**
- * The undo feature. It introduces the Undo and Redo buttons to the editor.
- *
- * Below is the explanation of the undo mechanism working together with {@link engine.model.History History}:
- *
- * Whenever a {@link engine.model.Delta delta} is applied to the {@link engine.model.Document document}, it is saved to
- * `History` as is. The {@link engine.model.Batch batch} that owns that delta is also saved, in {@link undo.UndoCommand},
- * together with the selection that was present in the document before the delta was applied. A batch is saved instead of the delta
- * because changes are undone batch-by-batch, not delta-by-delta and a batch is seen as one undo step.
- *
- * After some changes happen to the document, the `History` and `UndoCommand` stack can be represented as follows:
- *
- * History Undo stack
- * =========== ==================================
- * [delta A1] [batch A with selection before A1]
- * [delta B1] [batch B with selection before B1]
- * [delta B2] [batch C with selection before C1]
- * [delta C1]
- * [delta C2]
- * [delta B3]
- * [delta C3]
- *
- * Where deltas starting with the same letter are from same batch.
- *
- * Undoing a batch means that a set of deltas which will reverse the effects of that batch needs to be generated. For example, if a batch
- * added several letters, undoing the batch should remove them. It is important to apply undoing deltas in the reversed order,
- * so if a batch has delta `X`, `Y`, `Z`, reversed deltas `Zr`, `Yr` and `Xr` need to be applied. Otherwise reversed delta
- * `Xr` would operate on a wrong document state, because delta `X` does not know that deltas `Y` and `Z` happened.
- *
- * After deltas from an undone batch got {@link engine.model.Delta#getReversed reversed}, one needs to make sure if they are
- * ready to be applied. In the scenario above, delta `C3` is the last delta and `C3r` bases on up-to-date document state, so
- * it can be applied to the document.
+ * Includes a set of predefined autoformatting actions.
*
- * History Undo stack
- * =========== ==================================
- * [delta A1 ] [batch A with selection before A1]
- * [delta B1 ] [batch B with selection before B1]
- * [delta B2 ] [ processing undoing batch C ]
- * [delta C1 ]
- * [delta C2 ]
- * [delta B3 ]
- * [delta C3 ]
- * [delta C3r]
+ * ## Bulleted list
*
- * Next is delta `C2`, reversed to `C2r`. `C2r` bases on `C2`, so it bases on the wrong document state. It needs to be
- * transformed by deltas from history that happened after it, so it "knows" about them. Let us assume that `C2' = C2r * B3 * C3 * C3r`,
- * where `*` means "transformed by". As can be seen, `C2r` is transformed by a delta which is undone afterwards anyway.
- * This brings two problems: lower effectiveness (obvious) and incorrect results. Bad results come from the fact that
- * operational transformation algorithms assume there is no connection between two transformed operations when resolving
- * conflicts, which is true for example for collaborative editing, but is not true for the undo algorithm.
+ * You can create a bulleted list by staring a line with:
*
- * To prevent both problems, `History` introduces an API to {@link engine.model.History#removeDelta remove}
- * deltas from history. It is used to remove undone and undoing deltas after they are applied. It feels right — since when a
- * delta is undone or reversed, it is "removed" and there should be no sign of it in the history (fig. 1).
+ * * `* `
+ * * `- `
*
- * Notes:
+ * ## Numbered list
*
- * * `---` symbolizes a removed delta.
- * * `'` symbolizes a reversed delta that was later transformed.
+ * You can create a numbered list by staring a line with:
*
- * History (fig. 1) History (fig. 2) History (fig. 3)
- * ================ ================ ================
- * [delta A1] [delta A1] [delta A1]
- * [delta B1] [delta B1] [delta B1]
- * [delta B2] [delta B2] [delta B2]
- * [delta C1] [delta C1] [---C1---]
- * [delta C2] [---C2---] [---C2---]
- * [delta B3] [delta B3] [delta B3]
- * [---C3---] [---C3---] [---C3---]
- * [---C3r--] [---C3r--] [---C3r--]
- * [---C2'--] [---C2'--]
- * [---C1'--]
+ * * `1. `
+ * * `1) `
*
- * `C2r` can now be transformed only by `B3` and both `C2'` and `C2` can be removed (fig. 2). Same with `C1` (fig. 3).
+ * ## Headings
*
- * But what about that selection? For batch `C`, undo feature remembers the selection just before `C1` was applied. It can be
- * visualized between delta `B2` and `B3` (see fig. 3). As can be seen, some operations were applied to the document since the selection
- * state was remembered. Setting the document selection as it was remembered would be incorrect. It feels natural that
- * the selection state should also be transformed by deltas from history. The same pattern applies as with transforming deltas —
- * ranges should not be transformed by undone and undoing deltas. Thankfully, those deltas are already removed from history.
+ * You can create a heading by starting a line with:
*
- * Unfortunately, a problem appears with delta `B3`. It still remembers the context of deltas `C2` and `C1` on which it bases.
- * It is an obvious error — transforming by that delta would lead to incorrect results or "repeating" history would
- * produce a different document than the actual one.
+ * `# ` – will create Heading 1,
+ * `## ` – will create Heading 2,
+ * `### ` – will create Heading 3.
*
- * To prevent this situation, `B3` needs to also be {@link engine.model.History#updateDelta updated} in history.
- * It should be kept in a state that "does not remember" deltas that were removed from history. It is easily
- * achieved while transforming the reversed delta. For example, when `C2r` is transformed by `B3`, at the same time `B3` is
- * transformed by `C2r`. Transforming `B3` that remembers `C2` by a delta reversing `C2` effectively makes `B3` "forget" about `C2`.
- * By doing these transformations you effectively make `B3` base on `B2` which is the correct state of history (fig. 4).
+ * ## Bold and italic
*
- * History (fig. 4) History (fig. 5)
- * =========================== ===============================
- * [delta A1] [---A1---]
- * [delta B1] [delta B1 "without A1"]
- * [delta B2] [delta B2 "without A1"]
- * [---C1---] [---C1---]
- * [---C2---] [---C2---]
- * [delta B3 "without C2, C1"] [delta B3 "without C2, C1, A1"]
- * [---C3---] [---C3---]
- * [---C3r--] [---C3r--]
- * [---C2'--] [---C2'--]
- * [---C1'--] [---C1'--]
- * [---A1'--]
+ * You can apply bold or italic to a text by typing Markdown formatting:
*
- * Selective undo works on the same basis, however, instead of undoing the last batch in the undo stack, any batch can be undone.
- * The same algorithm applies: deltas from a batch (i.e. `A1`) are reversed and then transformed by deltas stored in history,
- * simultaneously updating them. Then deltas are applied to the document and removed from history (fig. 5).
+ * * `**foo bar**` or `__foo bar__` – will bold the text,
+ * * `*foo bar*` or `_foo bar_` – will italicize the text,
*
- * @memberOf undo
+ * @memberOf autoformat
* @extends core.Feature
*/
-var Undo = function (_Feature) {
- inherits(Undo, _Feature);
+var Autoformat = function (_Feature) {
+ inherits(Autoformat, _Feature);
- function Undo() {
- classCallCheck(this, Undo);
- return possibleConstructorReturn(this, Object.getPrototypeOf(Undo).apply(this, arguments));
- }
+ function Autoformat() {
+ classCallCheck(this, Autoformat);
+ return possibleConstructorReturn(this, Object.getPrototypeOf(Autoformat).apply(this, arguments));
+ }
- createClass(Undo, [{
- key: 'init',
+ createClass(Autoformat, [{
+ key: 'init',
- /**
- * @inheritDoc
- */
- value: function init() {
- var editor = this.editor;
- var t = editor.t;
+ /**
+ * @inheritDoc
+ */
+ value: function init() {
+ this._addListAutoformats();
+ this._addHeadingAutoformats();
+ this._addInlineAutoformats();
+ }
- this._addButton('undo', t('Undo'), 'CTRL+Z');
- this._addButton('redo', t('Redo'), 'CTRL+Y');
+ /**
+ * Adds autoformatting related to ListEngine commands.
+ *
+ * When typed:
+ * - `* ` or `- ` - paragraph will be changed to a bulleted list,
+ * - `1. ` or `1) ` - paragraph will be changed to a numbered list (1 can be any digit or list of digits).
+ *
+ * @private
+ */
- editor.keystrokes.set('CTRL+Z', 'undo');
- editor.keystrokes.set('CTRL+Y', 'redo');
- editor.keystrokes.set('CTRL+SHIFT+Z', 'redo');
- }
+ }, {
+ key: '_addListAutoformats',
+ value: function _addListAutoformats() {
+ new BlockAutoformatEngine(this.editor, /^[\*\-]\s$/, 'bulletedList');
+ new BlockAutoformatEngine(this.editor, /^\d+[\.|)]?\s$/, 'numberedList');
+ }
- /**
- * Creates a button for the specified command.
- *
- * @private
- * @param {String} name Command name.
- * @param {String} label Button label.
- * @param {String} keystroke Command keystroke.
- */
+ /**
+ * Adds autoformatting related to HeadingEngine commands.
+ * When typed `# ` or `## ` or `### ` paragraph will be changed to a corresponding heading level.
+ *
+ * @private
+ */
- }, {
- key: '_addButton',
- value: function _addButton(name, label, keystroke) {
- var editor = this.editor;
+ }, {
+ key: '_addHeadingAutoformats',
+ value: function _addHeadingAutoformats() {
+ var _this2 = this;
- var command = editor.commands.get(name);
+ new BlockAutoformatEngine(this.editor, /^(#{1,3})\s$/, function (context) {
+ var batch = context.batch;
+ var match = context.match;
- var model = new Model({
- isOn: false,
- label: label,
- icon: name,
- keystroke: keystroke
- });
+ var headingLevel = match[1].length;
- model.bind('isEnabled').to(command, 'isEnabled');
+ _this2.editor.execute('heading', {
+ batch: batch,
+ formatId: 'heading' + headingLevel
+ });
+ });
+ }
- this.listenTo(model, 'execute', function () {
- return editor.execute(name);
- });
+ /**
+ * Adds inline autoformatting capabilities to the editor.
+ *
+ * When typed:
+ * - `**foobar**`: `**` characters are removed, and `foobar` is set to bold,
+ * - `__foobar__`: `__` characters are removed, and `foobar` is set to bold,
+ * - `*foobar*`: `*` characters are removed, and `foobar` is set to italic,
+ * - `_foobar_`: `_` characters are removed, and `foobar` is set to italic.
+ *
+ * @private
+ */
- editor.ui.featureComponents.add(name, Button, ButtonView, model);
- }
- }], [{
- key: 'requires',
+ }, {
+ key: '_addInlineAutoformats',
+ value: function _addInlineAutoformats() {
+ new InlineAutoformatEngine(this.editor, /(\*\*)([^\*]+)(\*\*)$/g, 'bold');
+ new InlineAutoformatEngine(this.editor, /(__)([^_]+)(__)$/g, 'bold');
- /**
- * @inheritDoc
- */
- get: function get() {
- return [UndoEngine];
- }
- }]);
- return Undo;
+ // The italic autoformatter cannot be triggered by the bold markers, so we need to check the
+ // text before the pattern (e.g. `(?:^|[^\*])`).
+ new InlineAutoformatEngine(this.editor, /(?:^|[^\*])(\*)([^\*_]+)(\*)$/g, 'italic');
+ new InlineAutoformatEngine(this.editor, /(?:^|[^_])(_)([^_]+)(_)$/g, 'italic');
+ }
+ }], [{
+ key: 'requires',
+
+ /**
+ * @inheritDoc
+ */
+ get: function get() {
+ return [HeadingEngine, ListEngine, BoldEngine, ItalicEngine];
+ }
+ }]);
+ return Autoformat;
}(Feature);
// Babel helpers.
@@ -51969,7 +51969,7 @@ var ClassicEditor = function (_Classic) {
config.features = [];
}
- config.features = [].concat(toConsumableArray(config.features), [Autoformat, Bold, Italic, Clipboard, Enter, Heading, Link, List$1, Paragraph, Typing, Undo]);
+ config.features = [].concat(toConsumableArray(config.features), [Bold, Italic, Clipboard, Enter, Heading, Link, List$1, Paragraph, Typing, Undo, Autoformat]);
return ClassicEditor$1.create(element, config);
}
diff --git a/lib/ckeditor5/ckeditor.min.js b/lib/ckeditor5/ckeditor.min.js
index e84487621ba..b3dfe77bdde 100644
--- a/lib/ckeditor5/ckeditor.min.js
+++ b/lib/ckeditor5/ckeditor.min.js
@@ -1,11 +1,11 @@
var ClassicEditor=function(){"use strict";function t(){return function t(){t.called=!0}}function e(t){return t._events||Object.defineProperty(t,"_events",{value:{}}),t._events}function n(){return{callbacks:[],childEvents:[]}}function r(t,r){var i=e(t);if(!i[r]){for(var o=r,a=null,s=[];""!==o&&!i[o];)i[o]=n(),s.push(i[o]),a&&i[o].childEvents.push(a),a=o,o=o.substr(0,o.lastIndexOf(":"));if(""!==o){var u=!0,l=!1,c=void 0;try{for(var f,h=s[Symbol.iterator]();!(u=(f=h.next()).done);u=!0){var d=f.value;d.callbacks=i[o].callbacks.slice()}}catch(v){l=!0,c=v}finally{try{!u&&h["return"]&&h["return"]()}finally{if(l)throw c}}i[o].childEvents.push(a)}}}function i(t,n){var r=e(t)[n];if(!r)return[];for(var o=[r.callbacks],a=0;a-1?o(t,e.substr(0,e.lastIndexOf(":"))):null}function a(t){return us(Object(t))}function s(t){var e=!1;if(null!=t&&"function"!=typeof t.toString)try{e=!!(t+"")}catch(n){}return e}function u(t){return!!t&&"object"==("undefined"==typeof t?"undefined":Ya(t))}function l(t){if(!u(t)||vs.call(t)!=ls||s(t))return!1;var e=a(t);if(null===e)return!0;var n=hs.call(e,"constructor")&&e.constructor;return"function"==typeof n&&n instanceof n&&fs.call(n)==ds}function c(t,e){return t===e||t!==t&&e!==e}function f(t,e,n){var r=t[e];gs.call(t,e)&&c(r,n)&&(void 0!==n||e in t)||(t[e]=n)}function h(t,e,n,r){n||(n={});for(var i=-1,o=e.length;++i-1&&t%1==0&&t<=Os}function p(t){return null!=t&&m(bs(t))&&!y(t)}function g(t,e){return e=null==e?Ps:e,!!e&&("number"==typeof t||Ss.test(t))&&t>-1&&t%1==0&&t1?n[i-1]:void 0,a=i>2?n[2]:void 0;for(o=t.length>3&&"function"==typeof o?(i--,o):void 0,a&&b(n[0],n[1],a)&&(o=i<3?void 0:o,i=1),e=Object(e);++r1)throw new ms("observable-bind-to-no-callback: Binding multiple observables only possible with callback.");if(r>1&&e.callback)throw new ms("observable-bind-to-extra-callback: Cannot bind multiple attributes and use a callback in one binding.");e.to.forEach(function(e){if(e.attrs.length&&e.attrs.length!==r)throw new ms("observable-bind-to-attrs-length: The number of attributes must match.");e.attrs.length||(e.attrs=t._bindAttrs)}),this._to=e.to,e.callback&&(this._bindings.get(n[0]).callback=e.callback),$(this._observable,this._to),q(this),this._bindAttrs.forEach(function(e){H(t._observable,e)})}function D(t){return t.every(function(t){return"string"==typeof t})}function L(){for(var t=arguments.length,e=Array(t),n=0;n1?e-1:0),r=1;r-1}function oe(t,e){var n=this.__data__,r=ee(n,t);return r<0?n.push([t,e]):n[r][1]=e,this}function ae(t){var e=-1,n=t?t.length:0;for(this.clear();++es))return!1;var l=o.get(t);if(l)return l==e;var c=-1,f=!0,h=i&xf?new gn:void 0;for(o.set(t,e);++ci?0:i+e),n=n>i?i:n,n<0&&(n+=i),i=e>n?0:n-e>>>0,e>>>=0;for(var o=Array(i);++r0&&n(s)?e>1?Mn(s,e-1,n,r,i):De(i,s):r||(i[i.length]=s)}return i}function In(){for(var t=arguments.length,e=Array(t?t-1:0),n=arguments[0],r=t;r--;)e[r-1]=arguments[r];return t?De(ou(n)?Me(n):[n],Mn(e,1)):[]}function Bn(t,e,n){for(var r=t.length,i=e+(n?0:-1);n?i--:++i-1}function zn(t,e,n){for(var r=-1,i=t.length;++r=xh&&(o=$n,a=!1,e=new gn(e));t:for(;++i=e?t:e)),t}function _r(t){return t?kr(O(t),0,$h):0}function xr(t,e,n,r){var i=t.length;for(n=O(n),n<0&&(n=-n>i?0:i+n),r=void 0===r||r>i?i:O(r),r<0&&(r+=i),r=n>r?0:_r(r);n=120&&c.length>=120)?new gn(a&&c):void 0}c=t[0];var f=-1,h=s[0];t:for(;++f-1;)s!==t&&nd.call(s,u,1),nd.call(t,u,1);return t}function Hr(t,e){return t&&t.length&&e&&e.length?qr(t,e):t}function $r(t,e,n){return t&&t.length&&e&&e.length?qr(t,e,yr(n)):t}function Kr(t,e,n){return t&&t.length&&e&&e.length?qr(t,e,void 0,n):t}function Wr(t,e){for(var n=-1,r=null==t,i=e.length,o=Array(i);++ne||o&&a&&u&&!s&&!l||r&&a&&u||!n&&u||!i)return 1;if(!r&&!o&&!l&&t>>1,a=t[o];null!==a&&!k(a)&&(n?a<=e:a=pd){var l=e?null:md(t);if(l)return Xe(l);a=!1,i=$n,u=new gn}else u=e?[]:s;t:for(;++re._priority||!(t._priority1){var u=s[1];r.start.isTouching(u.end)?r.start=u.start:r.end.isTouching(u.start)&&(r.end=u.end)}}if(!r.start.isEqual(this.start)||!r.end.isEqual(this.end)){var l=Ru.createFromRange(this);this.start=r.start,this.end=r.end,this.fire("change",l)}}function Bi(t,e){if(t.size!=e.size)return!1;var n=!0,r=!1,i=void 0;try{for(var o,a=t.entries()[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value,u=JSON.stringify(s[1]),l=JSON.stringify(e.get(s[0]));if(u!==l)return!1}}catch(c){r=!0,i=c}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}return!0}function Di(t){return t instanceof Tu||t instanceof Pu?t.getAttributes():null}function Li(t,e){var n=arguments.length<=2||void 0===arguments[2]?{}:arguments[2];if(!e.isCollapsed){var r=e.getFirstRange(),i=r.start,o=Zu.createFromPosition(r.end);if(r.isEmpty||t.remove(r),n.merge){var a=o.path,s=Math.min(i.path.length-1,a.length-1),u=X(i.path,a);if("number"==typeof u)for(;u-1}function to(t){var e=void 0;if("string"==typeof t){if(e=Jd[t.toLowerCase()],!e)throw new ms("keyboard-unknown-key: Unknown key name.",{key:t})}else e=t.keyCode+(t.altKey?Jd.alt:0)+(t.ctrlKey?Jd.ctrl:0)+(t.shiftKey?Jd.shift:0);return e}function eo(t){return"string"==typeof t&&(t=io(t)),t.map(function(t){return"string"==typeof t?to(t):t}).reduce(function(t,e){return e+t},0)}function no(t){var e=io(t);return Wd.mac&&"ctrl"==e[0].toLowerCase()?"⌘"+(e[1]||""):t}function ro(){for(var t={arrowleft:37,arrowup:38,arrowright:39,arrowdown:40,backspace:8,"delete":46,enter:13,esc:27,tab:9,ctrl:1114112,cmd:1114112,shift:2228224,alt:4456448},e=65;e<=90;e++){var n=String.fromCharCode(e);t[n.toLowerCase()]=e}for(var r=48;r<=57;r++)t[r-48]=r;return t}function io(t){return t.split(/\s*\+\s*/)}function oo(t){return t instanceof Text&&t.data.substr(0,Xd)===Zd}function ao(t){return t.data.length==Xd&&oo(t)}function so(t){return oo(t)?t.data.slice(Xd):t.data}function uo(t,e){var n=ev.get(e);return n||(n=e(window.document),ev.set(e,n)),t.isEqualNode(n)}function lo(t){t.on("keydown",co)}function co(t,e){if(e.keyCode==Jd.arrowleft){var n=e.domTarget.ownerDocument.defaultView.getSelection();if(1==n.rangeCount&&n.getRangeAt(0).collapsed){var r=n.getRangeAt(0).startContainer,i=n.getRangeAt(0).startOffset;if(oo(r)&&i<=Xd){var o=new Range;o.setStart(r,0),o.collapse(!0),n.removeAllRanges(),n.addRange(o)}}}}function fo(t,e,n){function r(r){var a=(void 0!==f[r-1]?f[r-1]:-1)+1,l=void 0!==f[r+1]?f[r+1]:-1,h=a>l?-1:1;c[r+h]&&(c[r]=c[r+h].slice(0)),c[r]||(c[r]=[]),c[r].push(a>l?i:o);for(var d=Math.max(a,l),v=d-r;vl;d--)f[d]=r(d);f[l]=r(l),h++}while(f[l]!==u);return c[l].slice(1)}function ho(t,e,n){t.insertBefore(n,t.childNodes[e]||null)}function vo(t){var e=t.parentNode;e&&e.removeChild(t)}function yo(t){for(var e=0;t.previousSibling;)t=t.previousSibling,e++;return e}function mo(t){for(var e=[];t&&t.nodeType!=Node.DOCUMENT_NODE;)e.unshift(t),t=t.parentNode;return e}function po(t,e){for(var n=mo(t),r=mo(e),i=0;n[i]==r[i]&&n[i];)i++;return 0===i?null:n[i-1]}function go(t,e,n){var r=mo(t);return n&&(r=r.slice(r.indexOf(n)+1)),r.some(function(t){return t.tagName&&e.includes(t.tagName.toLowerCase())})}function bo(t){return t==Jd.arrowright||t==Jd.arrowleft||t==Jd.arrowup||t==Jd.arrowdown}function wo(t,e){return function(n,r){t.enqueueChanges(function(){var n=r.newSelection,i=[],o=!0,a=!1,s=void 0;try{for(var u,l=n.getRanges()[Symbol.iterator]();!(o=(u=l.next()).done);o=!0){var c=u.value;i.push(e.toModelRange(c))}}catch(f){a=!0,s=f}finally{try{!o&&l["return"]&&l["return"]()}finally{if(a)throw s}}t.selection.setRanges(i,n.isBackward)})}}function ko(){return function(t,e,n,r){var i=e.selection;if(!i.isCollapsed&&n.consume(i,"selection")){r.viewSelection.removeAllRanges();var o=!0,a=!1,s=void 0;try{for(var u,l=i.getRanges()[Symbol.iterator]();!(o=(u=l.next()).done);o=!0){var c=u.value,f=r.mapper.toViewRange(c);r.viewSelection.addRange(f,i.isBackward)}}catch(h){a=!0,s=h}finally{try{!o&&l["return"]&&l["return"]()}finally{if(a)throw s}}}}}function _o(){return function(t,e,n,r){var i=e.selection;if(i.isCollapsed&&n.consume(i,"selection")){var o=i.getFirstPosition(),a=r.mapper.toViewPosition(o),s=Ju.breakAttributes(a);r.viewSelection.removeAllRanges(),r.viewSelection.addRange(new Lu(s,s))}}}function xo(t){return function(e,n,r,i){var o=n.selection;if(o.isCollapsed&&r.consume(o,"selectionAttribute:"+n.key)){var a=i.viewSelection.getFirstPosition();i.viewSelection.removeAllRanges();var s=t instanceof Nu?t.clone(!0):t(n.value,n,o,r,i);a=Ju.wrapPosition(a,s),i.viewSelection.addRange(new Lu(a,a))}}}function Oo(){return function(t,e,n,r){var i=!0,o=!1,a=void 0;try{for(var s,u=r.viewSelection.getRanges()[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;l.isCollapsed&&l.end.parent.document&&Ju.mergeAttributes(l.start)}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}r.viewSelection.removeAllRanges()}}function Po(){return function(t,e,n,r){return r.viewSelection.setFake(!1)}}function So(t){return t instanceof HTMLTextAreaElement?t.value:t.innerHTML}function Ao(t,e){t instanceof HTMLTextAreaElement&&(t.value=e),t.innerHTML=e}function Co(t){return t.every(function(t){return"string"==typeof t})}function To(t){return t.name==wv}function Eo(t,e){return sn(t,!0,!0,e)}function Ro(t){return!!t&&(t.value&&(t=t.value),Array.isArray(t)?t.some(Ro):t instanceof Av)}function jo(t,e){return t.map(function(t){return t instanceof Av?t.getValue(e):t})}function Vo(t,e,n){var r=jo(t,e);r=1==t.length&&t[0]instanceof Tv?r[0]:r.reduce($o,""),Jo(r)?n.remove():n.set(r)}function Fo(t){return{set:function(e){t.textContent=e},remove:function(){t.textContent=""}}}function No(t,e){var n=arguments.length<=2||void 0===arguments[2]?null:arguments[2];return{set:function(r){t.setAttributeNS(n,e,r)},remove:function(){t.removeAttributeNS(n,e)}}}function Mo(t,e){return{set:function(n){t.style[e]=n},remove:function(){t.style[e]=null}}}function Io(t){var e=Eo(t,function(t){if(t&&(t instanceof Av||Uo(t)))return t});return e}function Bo(t){if("string"==typeof t?t=zo(t):t.text&&qo(t),t.on&&(t.eventListeners=Lo(t.on),delete t.on),!t.text){t.attributes&&Do(t.attributes);var e=new gv;if(t.children){var n=!0,r=!1,i=void 0;try{for(var o,a=t.children[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;e.add(Uo(s)?s:new Sv(s))}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}}t.children=e}return t}function Do(t){for(var e in t)t[e].value&&(t[e].value=[].concat(t[e].value)),Ho(t,e)}function Lo(t){for(var e in t)Ho(t,e);return t}function zo(t){return{text:[t]}}function qo(t){Array.isArray(t.text)||(t.text=[t.text])}function Ho(t,e){Array.isArray(t[e])||(t[e]=[t[e]])}function $o(t,e){return Jo(e)?t:Jo(t)?e:t+" "+e}function Ko(t,e){for(var n in e)if(t[n]){var r;(r=t[n]).push.apply(r,rs(e[n]))}else t[n]=e[n]}function Wo(t,e){if(e.attributes&&(t.attributes||(t.attributes={}),Ko(t.attributes,e.attributes)),e.eventListeners&&(t.eventListeners||(t.eventListeners={}),Ko(t.eventListeners,e.eventListeners)),e.text){var n;(n=t.text).push.apply(n,rs(e.text))}if(e.children&&e.children.length){if(t.children.length!=e.children.length)throw new ms("ui-template-extend-children-mismatch: The number of children in extended definition does not match.");var r=0,i=!0,o=!1,a=void 0;try{for(var s,u=e.children[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;Wo(t.children.get(r++),l)}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}}}function Jo(t){return!t&&0!==t}function Uo(t){return t instanceof Ev}function Go(t){return Rv.has("undefined"==typeof t?"undefined":Ya(t))&&t!==!1}function Yo(t){return function(e){return e+t}}function Qo(t,e,n){var r=[],i=!0,o=!1,a=void 0;try{for(var s,u=e[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){for(var l=s.value,c=new Eu({boundaries:l,mergeCharacters:!0}),f=c.next(),h=l.start,d=l.start,v=l.end;!f.done;){var y=f.value.item.name||"$text";n.check({name:y,inside:h,attributes:t})||(d.isEqual(h)||r.push(new Ru(d,h)),d=c.position),h=c.position,f=c.next()}d&&!d.isEqual(v)&&r.push(new Ru(d,v))}}catch(m){o=!0,a=m}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}return r}function Xo(t){return Array.from(t.getChildren()).reduce(function(t,e){return t+e.data},"")}function Zo(){return new ry}function ta(t,e){if("function"==typeof e)return e(t);var n={};return e.name&&(n.name=ea(e.name,t.name),!n.name)?null:e.attribute&&(n.attribute=na(e.attribute,t),!n.attribute)?null:!(e["class"]&&(n["class"]=ra(e["class"],t),!n["class"]))&&(!(e.style&&(n.style=ia(e.style,t),!n.style))&&n)}function ea(t,e){return t instanceof RegExp?t.test(e):t===e}function na(t,e){var n=[];for(var r in t){var i=t[r];if(!e.hasAttribute(r))return null;var o=e.getAttribute(r);if(i instanceof RegExp){if(!i.test(o))return null;n.push(r)}else{if(o!==i)return null;n.push(r)}}return n}function ra(t,e){var n=[],r=!0,i=!1,o=void 0;try{for(var a,s=t[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;if(u instanceof RegExp){var l=e.getClassNames(),c=!0,f=!1,h=void 0;try{for(var d,v=l[Symbol.iterator]();!(c=(d=v.next()).done);c=!0){var y=d.value;u.test(y)&&n.push(y)}}catch(m){f=!0,h=m}finally{try{!c&&v["return"]&&v["return"]()}finally{if(f)throw h}}if(0===n.length)return null}else{if(!e.hasClass(u))return null;n.push(u)}}}catch(m){i=!0,o=m}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return n}function ia(t,e){var n=[];for(var r in t){var i=t[r];if(!e.hasStyle(r))return null;var o=e.getStyle(r);if(i instanceof RegExp){if(!i.test(o))return null;n.push(r)}else{if(o!==i)return null;n.push(r)}}return n}function oa(t,e,n,r){if(U(t)){var i=!0,o=!1,a=void 0;try{for(var s,u=t[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;oa(l,e,n,r)}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}}else{var f=Array.from(t.getAttributeKeys());f.push(e.key);var h={name:t.name||"$text",attributes:f,inside:n.context};r.schema.check(h)&&t.setAttribute(e.key,e.value)}}function aa(){return new oy}function sa(t,e){e.isEnabled=!1}function ua(t){var e=arguments.length<=1||void 0===arguments[1]||arguments[1],n=t.parent;if(n instanceof Xu)return e?t.nodeAfter:t.nodeBefore;for(;!(n.parent instanceof Xu);)n=n.parent;return n}function la(t){return Array.from(t.getAncestors()).find(function(t){return"listItem"==t.name})||null}function ca(t,e){for(var n=fa(t.getFirstPosition(),e),r=t.getLastPosition(),i=[];null!==n&&n.isBefore(r);)i.push(n.nodeAfter),n.offset++,n=fa(n,e);return i}function fa(t,e){var n=t.nodeAfter;for(n||(n=t.parent);null!==n&&!e.itemExtends(n.name||"$text","$block");)n=n.parent;return null!==n&&null!==n.parent?Cu.createBefore(n):null}function ha(t,e){var n="numbered"==t.getAttribute("type")?"ol":"ul",r=new dy,i=new Mu(n,null);return i.appendChildren(r),e.bindElements(t,r),r}function da(t,e){for(var n=e.getNext?"nextSibling":"previousSibling",r=!!e.checkAllSiblings,i=!!e.sameIndent,o=!!e.biggerIndent,a=t.getAttribute("indent"),s=t[n];s&&"listItem"==s.name;){var u=s.getAttribute("indent");if(i&&a==u||o&&au)return null;s=s[n]}return null}function va(t,e){t&&e&&("ul"==t.name||"ol"==t.name)&&t.name==e.name&&Ju.mergeContainers(Bu.createAfter(t))}function ya(t,e,n){var r=e.parent,i=da(t,{sameIndent:!0,biggerIndent:!0});if(i){var o=n.toViewElement(i),a=Bu.createAfter(o);Ju.breakContainer(a)}var s=da(t,{sameIndent:!0,checkAllSiblings:!0}),u=void 0;if(s){var l=n.toViewElement(s),c=Bu.createAfter(l);u=Ju.breakContainer(c)}else{var f=t.previousSibling;u=f&&"listItem"==f.name?Bu.createAt(n.toViewElement(f),"end"):n.toViewPosition(Cu.createBefore(t))}Ju.insert(u,r);var h=da(t,{getNext:!0,biggerIndent:!0}),d=n.toViewElement(h);if(d){var v=Lu.createOn(d.parent),y=Bu.createAt(e,"end");Ju.move(v,y)}va(r,r.nextSibling),va(r.previousSibling,r)}function ma(t,e,n,r){if(n.test(e.item,"insert")&&n.test(e.item,"addAttribute:type")&&n.test(e.item,"addAttribute:indent")){n.consume(e.item,"insert"),n.consume(e.item,"addAttribute:type"),n.consume(e.item,"addAttribute:indent");var i=e.item,o=ha(i,r.mapper);ya(i,o,r.mapper)}}function pa(t,e,n,r){if(n.consume(e.item,"changeAttribute:type")){var i=r.mapper.toViewElement(e.item);Ju.breakContainer(Bu.createBefore(i)),Ju.breakContainer(Bu.createAfter(i));var o=i.parent,a="numbered"==e.attributeNewValue?"ol":"ul";o=Ju.rename(o,a),va(o,o.nextSibling),va(o.previousSibling,o)}}function ga(t,e,n,r){if(n.consume(e.item,"remove")){var i=r.mapper.toViewElement(e.item);Ju.breakContainer(Bu.createBefore(i)),Ju.breakContainer(Bu.createAfter(i));var o=i.parent;Ju.remove(Lu.createOn(o))}}function ba(t,e,n,r){if(n.consume(e.item,"move")){var i=r.mapper.toViewElement(e.item);Ju.breakContainer(Bu.createBefore(i)),Ju.breakContainer(Bu.createAfter(i));var o=i.parent,a=o.previousSibling,s=o.nextSibling,u=r.mapper.toViewPosition(e.targetPosition);"ol"!=u.parent.name&&"ul"!=u.parent.name||(u=Ju.breakContainer(u)),Ju.move(Lu.createOn(o),u),va(a,s),va(o,o.nextSibling),va(o.previousSibling,o)}}function wa(t,e,n,r){if(n.consume(e.item,"changeAttribute:indent")){var i=r.mapper.toViewElement(e.item);Ju.breakContainer(Bu.createBefore(i)),Ju.breakContainer(Bu.createAfter(i));var o=i.parent,a=o.previousSibling;Ju.remove(Lu.createOn(o)),va(a,a.nextSibling),ya(e.item,i,r.mapper)}}function ka(t,e,n,r){if("listItem"!=e.item.name)for(var i=r.mapper.toViewPosition(e.range.start);("ul"==i.parent.name||"ol"==i.parent.name)&&(i=Ju.breakContainer(i),null!==i.parent.parent);)i=Bu.createBefore(i.parent)}function _a(t,e,n,r){var i=r.mapper.toViewPosition(e.sourcePosition),o=i.nodeBefore,a=i.nodeAfter;va(o,a)}function xa(t,e,n,r){if(n.consume(e.input,{name:!0})){var i=new Su("listItem");e.indent=e.indent?e.indent:0;var o="ul"==e.input.parent.name?"bulleted":"numbered";i.setAttribute("type",o),i.setAttribute("indent",e.indent),e.context.push(i),e.indent++;var a=[i],s=!0,u=!1,l=void 0;try{for(var c,f=e.input.getChildren()[Symbol.iterator]();!(s=(c=f.next()).done);s=!0){var h=c.value,d=r.convertItem(h,n,e);"ul"==h.name||"ol"==h.name?a=a.concat(Array.from(d.getChildren())):i.appendChildren(d)}}catch(v){u=!0,l=v}finally{try{!s&&f["return"]&&f["return"]()}finally{if(u)throw l}}e.indent--,e.context.pop(),e.output=e.output?e.output.concat(a):a}}function Oa(t,e,n){if(n.test(e.input,{name:!0})){var r=Array.from(e.input.getChildren()),i=!0,o=!1,a=void 0;try{for(var s,u=r[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;l.name&&"li"==l.name||l.remove()}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}}}function Pa(t,e){var n=e.modelPosition,r=e.mapper,i=n.nodeAfter;if(i&&"listItem"==i.name){var o=r.toViewElement(i);o&&0!==o.index&&(e.viewPosition=Bu.createBefore(o),t.stop())}}function Sa(t,e){var n=e.viewPosition,r=e.mapper,i=n.nodeAfter,o=n.nodeBefore,a=void 0;if(i)"ul"==i.name||"ol"==i.name?a=r.toModelElement(i.getChild(0)):"li"==i.name&&(a=r.toModelElement(i)),a&&(e.modelPosition=Cu.createBefore(a));else if(o){var s=void 0;if("ul"==o.name||"ol"==o.name?s=o.getChild(o.childCount-1):"li"==o.name&&(s=o),s){a=r.toModelElement(s);
-var u=r.getModelLength(s);e.modelPosition=Cu.createBefore(a).getShiftedBy(u)}}null!==e.modelPosition&&t.stop()}function Aa(t,e,n){if(e.isCollapsed)return n.check({name:"$text",inside:e.getFirstPosition(),attributes:t});var r=e.getRanges(),i=!0,o=!1,a=void 0;try{for(var s,u=r[Symbol.iterator]();!(i=(s=u.next()).done);i=!0)for(var l=s.value,c=new Eu({boundaries:l,mergeCharacters:!0}),f=c.position,h=c.next();!h.done;){var d=h.value.item.name||"$text";if(n.check({name:d,inside:f,attributes:t}))return!0;f=c.position,h=c.next()}}catch(v){o=!0,a=v}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}return!1}function Ca(t){return t=t.replace(//g,">").replace(/\n\n/g,"
").replace(/\n/g," ").replace(/^\s/," ").replace(/\s$/," ").replace(/\s\s/g," "),t.indexOf("
")>-1&&(t="
"+t+"
"),t}function Ta(t){return t.replace(/(\s+)<\/span>/g,function(t,e){return 1==e.length?" ":e})}function Ea(t,e){var n=t.document,r=e.isCollapsed,i=e.getFirstRange(),o=i.start.parent,a=i.end.parent;if(o.root==o)return void(r||n.composer.deleteContents(t,e));if(r)Ra(t,e,i.start);else{var s=i.start.isAtStart&&i.end.isAtEnd,u=o==a;n.composer.deleteContents(t,e,{merge:s}),s||(u?Ra(t,e,e.focus):e.collapse(a))}}function Ra(t,e,n){t.split(n),e.collapse(n.parent.nextSibling)}function ja(t,e){return new Ru(Va(t,e,!0),Va(t,e,!1))}function Va(t,e,n){for(var r=t.textNode||(n?t.nodeBefore:t.nodeAfter),i=null;r&&r.getAttribute("linkHref")==e;)i=r,r=n?r.previousSibling:r.nextSibling;return i?Cu.createAt(i,n?"before":"after"):t}function Fa(t){var e=t.controller,n=function(e,n){return Na(n.target,t.contextElement,t.callback)};e.listenTo(t.model,"change:"+t.activeIf,function(t,r,i){i?e.listenTo(document,"mouseup",n):e.stopListening(document,"mouseup",n)}),t.model[t.activeIf]&&e.listenTo(document,"mouseup",n)}function Na(t,e,n){e.contains(t)||n()}function Ma(t){var e=t.controller,n=function(e,n){return Ia(n.keyCode,t.callback)};e.listenTo(t.model,"change:"+t.activeIf,function(t,r,i){i?e.listenTo(document,"keydown",n):e.stopListening(document,"keydown",n)}),t.model[t.activeIf]&&e.listenTo(document,"keydown",n)}function Ia(t,e){t==Jd.esc&&e()}function Ba(t){var e=document.body.getBoundingClientRect();if(t instanceof HTMLElement||t instanceof Range){var n=t.getBoundingClientRect();return{top:n.top-e.top,right:n.right-e.left,bottom:n.bottom-e.top,left:n.left-e.left,width:n.width,height:n.height}}var r=Object.assign({},t);return void 0===r.width&&(r.width=r.right-r.left),void 0===r.height&&(r.height=r.bottom-r.top),r}function Da(t){var e=Ba(t),n=La();return new em({top:Math.max(e.top,n.top),left:Math.max(e.left,n.left),right:Math.min(e.right,n.right),bottom:Math.min(e.bottom,n.bottom)})}function La(){var t=window.scrollX,e=window.scrollY,n=window.innerWidth,r=window.innerHeight;return{top:e,right:n+t,bottom:r+e,left:t}}function za(t){var e=t.view;e.listenTo(e.element,"submit",function(t,n){n.preventDefault(),e.fire("submit")},{useCapture:!0})}function qa(t){return t.parent.getAncestors().find(function(t){return t instanceof Wy})}function Ha(t){var e=0,n=!0,r=!1,i=void 0;try{for(var o,a=t[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){o.value;e++}}catch(s){r=!0,i=s}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}return e}function $a(t,e){function n(){a&&(i.push(a),a=null)}function r(t){return a&&a.type==t}var i=[],o=0,a=void 0;return t.forEach(function(t){"equal"==t?(n(),o++):"insert"==t?(r("insert")?a.values.push(e[o]):(n(),a={type:"insert",index:o,values:[e[o]]}),o++):r("delete")?a.howMany++:(n(),a={type:"delete",index:o,howMany:1})}),n(),i}function Ka(t){return!!t.ctrlKey||ym.includes(t.keyCode)}function Wa(t,e){return t instanceof Vu&&e instanceof Vu?t.data===e.data:t===e}function Ja(t,e){var n=!(arguments.length<=2||void 0===arguments[2])&&arguments[2],r=[],i=!0,o=!1,a=void 0;try{for(var s,u=t[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){for(var l=s.value,c=[l],f=0;f=0;--r){var i=this.tryEntries[r],o=i.completion;if("root"===i.tryLoc)return e("end");if(i.tryLoc<=this.prev){var a=m.call(i,"catchLoc"),s=m.call(i,"finallyLoc");if(a&&s){if(this.prev=0;--n){var r=this.tryEntries[n];if(r.tryLoc<=this.prev&&m.call(r,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),f(n),S}},"catch":function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var r=n.completion;if("throw"===r.type){var i=r.arg;f(n)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,n){return this.delegate={iterator:d(t),resultName:e,nextLoc:n},S}}}("object"===("undefined"==typeof global?"undefined":Ya(global))?global:"object"===("undefined"==typeof window?"undefined":Ya(window))?window:"object"===("undefined"==typeof self?"undefined":Ya(self))?self:void 0);var is=function Cm(e,n){Qa(this,Cm),this.source=e,this.name=n,this.path=[],this.stop=t(),this.off=t()},os=function(){var t=1;return function(){return t++}}(),as={get:function(t){return"number"!=typeof t?this[t]||this.normal:t},highest:1e5,high:1e3,normal:0,low:-1e3,lowest:-1e5},ss={on:function(t,e){var n=arguments.length<=2||void 0===arguments[2]?{}:arguments[2];r(this,t);var o=i(this,t),a=as.get(n.priority);e={callback:e,context:n.context||this,priority:a};var s=!0,u=!1,l=void 0;try{for(var c,f=o[Symbol.iterator]();!(s=(c=f.next()).done);s=!0){for(var h=c.value,d=!1,v=0;v1?i-1:0),s=1;s=e&&t0){t.children=[];var n=!0,r=!1,i=void 0;try{for(var o,a=this._children[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;t.children.push(s.toJSON())}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}}return t}},{key:"childCount",get:function(){return this._children.length}},{key:"maxOffset",get:function(){return this._children.maxOffset}},{key:"isEmpty",get:function(){return 0===this.childCount}}],[{key:"fromJSON",value:function(t){var n=null;if(t.children){n=[];var r=!0,i=!1,o=void 0;try{for(var a,s=t.children[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.name?n.push(e.fromJSON(u)):n.push(Pu.fromJSON(u))}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}}return new e(t.name,t.attributes,n)}}]),e}(xu),Au=function(){function t(e){Qa(this,t),this._children=new Ou,e&&this.insertChildren(0,e)}return Xa(t,[{key:Symbol.iterator,value:function(){return this.getChildren()}},{key:"getChild",value:function(t){return this._children.getNode(t)}},{key:"getChildren",value:function(){return this._children[Symbol.iterator]()}},{key:"getChildIndex",value:function(t){return this._children.getNodeIndex(t)}},{key:"getChildStartOffset",value:function(t){return this._children.getNodeStartOffset(t)}},{key:"getPath",value:function(){return[]}},{key:"offsetToIndex",value:function(t){return this._children.offsetToIndex(t)}},{key:"appendChildren",value:function(t){this.insertChildren(this.childCount,t)}},{key:"insertChildren",value:function(t,e){e=Y(e);var n=!0,r=!1,i=void 0;try{for(var o,a=e[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;s.parent=this}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}this._children.insertNodes(t,e)}},{key:"removeChildren",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?1:arguments[1],n=this._children.removeNodes(t,e),r=!0,i=!1,o=void 0;try{for(var a,s=n[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.parent=null}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return n}},{key:"toJSON",value:function(){var t=[],e=!0,n=!1,r=void 0;try{for(var i,o=this._children[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;t.push(a.toJSON())}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}return t}},{key:"childCount",get:function(){return this._children.length}},{key:"maxOffset",get:function(){return this._children.maxOffset}},{key:"isEmpty",get:function(){return 0===this.childCount}},{key:"root",get:function(){return this}},{key:"parent",get:function(){return null}}],[{key:"fromJSON",value:function(e){var n=[],r=!0,i=!1,o=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.name?n.push(Su.fromJSON(u)):n.push(Pu.fromJSON(u))}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return new t(n)}}]),t}(),Cu=function(){function t(e,n){if(Qa(this,t),!(e instanceof Su||e instanceof Au))throw new ms("model-position-root-invalid: Position root invalid.");if(!(n instanceof Array)||0===n.length)throw new ms("model-position-path-incorrect: Position path must be an Array with at least one item.",{path:n});n=e.getPath().concat(n),e=e.root,this.root=e,this.path=n}return Xa(t,[{key:"compareWith",value:function(t){if(this.root!=t.root)return"different";
-var e=X(this.path,t.path);switch(e){case"same":return"same";case"prefix":return"before";case"extension":return"after";default:return this.path[e]r.path.length){if(n.offset!==o.maxOffset)return!1;n.path=n.path.slice(0,-1),o=o.parent,n.offset++}else{if(0!==r.offset)return!1;r.path=r.path.slice(0,-1)}}}},{key:"_getTransformedByDeletion",value:function(e,n){var r=t.createFromPosition(this);if(this.root!=e.root)return r;if("same"==X(e.getParentPath(),this.getParentPath())){if(e.offsetthis.offset)return null;r.offset-=n}}else if("prefix"==X(e.getParentPath(),this.getParentPath())){var i=e.path.length-1;if(e.offset<=this.path[i]){if(e.offset+n>this.path[i])return null;r.path[i]-=n}}return r}},{key:"_getTransformedByInsertion",value:function(e,n,r){var i=t.createFromPosition(this);if(this.root!=e.root)return i;if("same"==X(e.getParentPath(),this.getParentPath()))(e.offsete.offsetSize)throw new ms("model-textproxy-wrong-offsetintext: Given offsetInText value is incorrect.");if(r<0||n+r>e.offsetSize)throw new ms("model-textproxy-wrong-length: Given length value is incorrect.");this.data=e.data.substring(n,n+r),this.offsetInText=n}return Xa(t,[{key:"getPath",value:function(){var t=this.textNode.getPath();return t.length>0&&(t[t.length-1]+=this.offsetInText),t}},{key:"getAncestors",value:function(){for(var t=arguments.length<=0||void 0===arguments[0]?{includeNode:!1,parentFirst:!1}:arguments[0],e=[],n=t.includeNode?this:this.parent;n;)e[t.parentFirst?"push":"unshift"](n),n=n.parent;return e}},{key:"hasAttribute",value:function(t){return this.textNode.hasAttribute(t)}},{key:"getAttribute",value:function(t){return this.textNode.getAttribute(t)}},{key:"getAttributes",value:function(){return this.textNode.getAttributes()}},{key:"getAttributeKeys",value:function(){return this.textNode.getAttributeKeys()}},{key:"startOffset",get:function(){return null!==this.textNode.startOffset?this.textNode.startOffset+this.offsetInText:null}},{key:"offsetSize",get:function(){return this.data.length}},{key:"endOffset",get:function(){return null!==this.startOffset?this.startOffset+this.offsetSize:null}},{key:"isPartial",get:function(){return this.offsetSize!==this.textNode.offsetSize}},{key:"parent",get:function(){return this.textNode.parent}},{key:"root",get:function(){return this.textNode.root}},{key:"document",get:function(){return this.textNode.document}}]),t}(),Eu=function(){function t(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];if(Qa(this,t),!e.boundaries&&!e.startPosition)throw new ms("model-tree-walker-no-start-position: Neither boundaries nor starting position have been defined.");var n=e.direction||"forward";if("forward"!=n&&"backward"!=n)throw new ms("model-tree-walker-unknown-direction: Only `backward` and `forward` direction allowed.",{direction:n});this.direction=n,this.boundaries=e.boundaries||null,e.startPosition?this.position=Cu.createFromPosition(e.startPosition):this.position=Cu.createFromPosition(this.boundaries["backward"==this.direction?"end":"start"]),this.singleCharacters=!!e.singleCharacters,this.shallow=!!e.shallow,this.ignoreElementEnd=!!e.ignoreElementEnd,this._boundaryStartParent=this.boundaries?this.boundaries.start.parent:null,this._boundaryEndParent=this.boundaries?this.boundaries.end.parent:null,this._visitedParent=this.position.parent}return Xa(t,[{key:Symbol.iterator,value:function(){return this}},{key:"next",value:function(){return"forward"==this.direction?this._next():this._previous()}},{key:"_next",value:function(){var t=this.position,e=Cu.createFromPosition(this.position),n=this._visitedParent;if(null===n.parent&&e.offset===n.maxOffset)return{done:!0};if(n===this._boundaryEndParent&&e.offset==this.boundaries.end.offset)return{done:!0};var r=e.textNode?e.textNode:e.nodeAfter;if(r instanceof Su)return this.shallow?e.offset++:(e.path.push(0),this._visitedParent=r),this.position=e,Z("elementStart",r,t,e,1);if(r instanceof Pu){var i=void 0,o=void 0;if(this.singleCharacters)i=1;else{var a=r.endOffset;this._boundaryEndParent==n&&this.boundaries.end.offseta&&(a=this.boundaries.start.offset),i=e.offset-a}o=e.offset-r.startOffset;var s=new Tu(r,o-i,i);return e.offset-=i,this.position=e,Z("text",s,t,e,i)}return e.path.pop(),this.position=e,this._visitedParent=n.parent,Z("elementStart",n,t,e,1)}}]),t}(),Ru=function(){function t(e){var n=arguments.length<=1||void 0===arguments[1]?null:arguments[1];Qa(this,t),this.start=Cu.createFromPosition(e),this.end=n?Cu.createFromPosition(n):Cu.createFromPosition(e)}return Xa(t,[{key:Symbol.iterator,value:regeneratorRuntime.mark(function e(){return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.delegateYield(new Eu({boundaries:this,ignoreElementEnd:!0}),"t0",1);case 1:case"end":return t.stop()}},e,this)})},{key:"containsPosition",value:function(t){return t.isAfter(this.start)&&t.isBefore(this.end)}},{key:"containsRange",value:function(t){return this.containsPosition(t.start)&&this.containsPosition(t.end)}},{key:"isEqual",value:function(t){return this.start.isEqual(t.start)&&this.end.isEqual(t.end)}},{key:"isIntersecting",value:function(t){return this.start.isBefore(t.end)&&this.end.isAfter(t.start)}},{key:"getDifference",value:function(e){var n=[];return this.isIntersecting(e)?(this.containsPosition(e.start)&&n.push(new t(this.start,e.start)),this.containsPosition(e.end)&&n.push(new t(e.end,this.end))):n.push(t.createFromRange(this)),n}},{key:"getIntersection",value:function(e){if(this.isIntersecting(e)){var n=this.start,r=this.end;return this.containsPosition(e.start)&&(n=e.start),this.containsPosition(e.end)&&(r=e.end),new t(n,r)}return null}},{key:"getMinimalFlatRanges",value:function(){for(var e=[],n=X(this.start.path,this.end.path),r="string"==typeof n?Math.min(this.start.path.length,this.end.path.length):n,i=Cu.createFromPosition(this.start),o=i.parent;i.path.length>r+1;){var a=o.maxOffset-i.offset;0!==a&&e.push(new t(i,i.getShiftedBy(a))),i.path=i.path.slice(0,-1),i.offset++,o=o.parent}for(;i.path.length<=this.end.path.length;){var s=this.end.path[i.path.length-1],u=s-i.offset;0!==u&&e.push(new t(i,i.getShiftedBy(u))),i.offset=s,i.path.push(0)}return e}},{key:"getWalker",value:function(){var t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return t.boundaries=this,new Eu(t)}},{key:"getItems",value:regeneratorRuntime.mark(function n(){var t,e,r,i,o,a,s,u=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return regeneratorRuntime.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:u.boundaries=this,u.ignoreElementEnd=!0,t=new Eu(u),e=!0,r=!1,i=void 0,n.prev=6,o=t[Symbol.iterator]();case 8:if(e=(a=o.next()).done){n.next=15;break}return s=a.value,n.next=12,s.item;case 12:e=!0,n.next=8;break;case 15:n.next=21;break;case 17:n.prev=17,n.t0=n["catch"](6),r=!0,i=n.t0;case 21:n.prev=21,n.prev=22,!e&&o["return"]&&o["return"]();case 24:if(n.prev=24,!r){n.next=27;break}throw i;case 27:return n.finish(24);case 28:return n.finish(21);case 29:case"end":return n.stop()}},n,this,[[6,17,21,29],[22,,24,28]])})},{key:"getPositions",value:regeneratorRuntime.mark(function r(){var t,e,n,i,o,a,s,u=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return u.boundaries=this,t=new Eu(u),r.next=4,t.position;case 4:e=!0,n=!1,i=void 0,r.prev=7,o=t[Symbol.iterator]();case 9:if(e=(a=o.next()).done){r.next=16;break}return s=a.value,r.next=13,s.nextPosition;case 13:e=!0,r.next=9;break;case 16:r.next=22;break;case 18:r.prev=18,r.t0=r["catch"](7),n=!0,i=r.t0;case 22:r.prev=22,r.prev=23,!e&&o["return"]&&o["return"]();case 25:if(r.prev=25,!n){r.next=28;break}throw i;case 28:return r.finish(25);case 29:return r.finish(22);case 30:case"end":return r.stop()}},r,this,[[7,18,22,30],[23,,25,29]])})},{key:"_getTransformedByInsertion",value:function(e,n){var r=!(arguments.length<=2||void 0===arguments[2])&&arguments[2],i=!(arguments.length<=3||void 0===arguments[3])&&arguments[3];if(r&&this.containsPosition(e))return[new t(this.start,e),new t(e._getTransformedByInsertion(e,n,!0),this.end._getTransformedByInsertion(e,n,this.isCollapsed))];var o=t.createFromRange(this),a=o.isCollapsed?i:!i,s=i;return o.start=o.start._getTransformedByInsertion(e,n,a),o.end=o.end._getTransformedByInsertion(e,n,s),[o]}},{key:"_getTransformedByMove",value:function(e,n,r,i){var o=!(arguments.length<=4||void 0===arguments[4])&&arguments[4],a=void 0,s=new t(e,e.getShiftedBy(r)),u=this.getDifference(s),l=void 0;l=1==u.length?new t(u[0].start._getTransformedByDeletion(e,r),u[0].end._getTransformedByDeletion(e,r)):2==u.length?new t(this.start,this.end._getTransformedByDeletion(e,r)):null;var c=n._getTransformedByDeletion(e,r);a=l?l._getTransformedByInsertion(c,r,i,o):[];var f=this.getIntersection(s);return!f||!i&&null!==l&&l.containsPosition(c)||a.push(new t(f.start._getCombined(s.start,c),f.end._getCombined(s.start,c))),a}},{key:"isCollapsed",get:function(){return this.start.isEqual(this.end)}},{key:"isFlat",get:function(){return this.start.parent===this.end.parent}},{key:"isEmpty",get:function(){return this.start.isTouching(this.end)}},{key:"root",get:function(){return this.start.root}}],[{key:"createFromPositionAndShift",value:function(t,e){var n=t,r=t.getShiftedBy(e);return e>0?new this(n,r):new this(r,n)}},{key:"createFromParentsAndOffsets",value:function(t,e,n,r){return new this(Cu.createFromParentAndOffset(t,e),Cu.createFromParentAndOffset(n,r))}},{key:"createFromRange",value:function(t){return new this(t.start,t.end)}},{key:"createIn",value:function(t){return this.createFromParentsAndOffsets(t,0,t,t.maxOffset)}},{key:"createOn",value:function(t){return this.createFromPositionAndShift(Cu.createBefore(t),t.offsetSize)}},{key:"fromJSON",value:function(t,e){return new this(Cu.fromJSON(t.start,e),Cu.fromJSON(t.end,e))}}]),t}(),ju=function(){function t(){Qa(this,t),this.parent=null}return Xa(t,[{key:"getAncestors",value:function(){for(var t=arguments.length<=0||void 0===arguments[0]?{includeNode:!1,parentFirst:!1}:arguments[0],e=[],n=t.includeNode?this:this.parent;n;)e[t.parentFirst?"push":"unshift"](n),n=n.parent;return e}},{key:"remove",value:function(){this.parent.removeChildren(this.index)}},{key:"_fireChange",value:function(t,e){this.fire("change:"+t,e),this.parent&&this.parent._fireChange(t,e)}},{key:"index",get:function(){var t=void 0;if(!this.parent)return null;if((t=this.parent.getChildIndex(this))==-1)throw new ms("view-node-not-found-in-parent: The node's parent does not contain this node.");return t}},{key:"nextSibling",get:function(){var t=this.index;return null!==t&&this.parent.getChild(t+1)||null}},{key:"previousSibling",get:function(){var t=this.index;return null!==t&&this.parent.getChild(t-1)||null}},{key:"root",get:function(){for(var t=this;t.parent;)t=t.parent;return t}},{key:"document",get:function(){return this.parent instanceof t?this.parent.document:null}}]),t}();K(ju,ss);var Vu=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this));return n._data=t,n}return ts(e,t),Xa(e,[{key:"clone",value:function(){return new e(this.data)}},{key:"isSimilar",value:function(t){return t instanceof e&&(this===t||this.data===t.data)}},{key:"data",get:function(){return this._data},set:function(t){this._fireChange("text",this),this._data=t}}]),e}(ju),Fu=function(){function t(e,n,r){if(Qa(this,t),this.textNode=e,n<0||n>e.data.length)throw new ms("view-textproxy-wrong-offsetintext: Given offsetInText value is incorrect.");if(r<0||n+r>e.data.length)throw new ms("view-textproxy-wrong-length: Given length value is incorrect.");this.data=e.data.substring(n,n+r),this.offsetInText=n}return Xa(t,[{key:"getAncestors",value:function(){for(var t=arguments.length<=0||void 0===arguments[0]?{includeNode:!1,parentFirst:!1}:arguments[0],e=[],n=t.includeNode?this.textNode:this.parent;null!==n;)e[t.parentFirst?"push":"unshift"](n),n=n.parent;return e}},{key:"isPartial",get:function(){return this.data.length!==this.textNode.data.length}},{key:"parent",get:function(){return this.textNode.parent}},{key:"root",get:function(){return this.textNode.root}},{key:"document",get:function(){return this.textNode.document}}]),t}(),Nu=function(t){function e(t,n,r){Qa(this,e);var i=es(this,Object.getPrototypeOf(e).call(this));if(i.name=t,l(n)?i._attrs=W(n):i._attrs=new Map(n),i._children=[],r&&i.insertChildren(0,r),i._classes=new Set,i._attrs.has("class")){var o=i._attrs.get("class");et(i._classes,o),i._attrs["delete"]("class")}return i._styles=new Map,i._attrs.has("style")&&(tt(i._styles,i._attrs.get("style")),i._attrs["delete"]("style")),i}return ts(e,t),Xa(e,[{key:"clone",value:function(t){var e=[];if(t){var n=!0,r=!1,i=void 0;try{for(var o,a=this.getChildren()[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;e.push(s.clone(t))}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}}var l=new this.constructor(this.name,this._attrs,e);return l._classes=new Set(this._classes),l._styles=new Map(this._styles),l}},{key:"appendChildren",value:function(t){return this.insertChildren(this.childCount,t)}},{key:"getChild",value:function(t){return this._children[t]}},{key:"getChildIndex",value:function(t){return this._children.indexOf(t)}},{key:"getChildren",value:function(){return this._children[Symbol.iterator]()}},{key:"getAttributeKeys",value:regeneratorRuntime.mark(function n(){var t,e,r,i,o,a;return regeneratorRuntime.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:if(!(this._classes.size>0)){n.next=3;break}return n.next=3,"class";case 3:if(!(this._styles.size>0)){n.next=6;break}return n.next=6,"style";case 6:t=!0,e=!1,r=void 0,n.prev=9,i=this._attrs.keys()[Symbol.iterator]();case 11:if(t=(o=i.next()).done){n.next=18;break}return a=o.value,n.next=15,a;case 15:t=!0,n.next=11;break;case 18:n.next=24;break;case 20:n.prev=20,n.t0=n["catch"](9),e=!0,r=n.t0;case 24:n.prev=24,n.prev=25,!t&&i["return"]&&i["return"]();case 27:if(n.prev=27,!e){n.next=30;break}throw r;case 30:return n.finish(27);case 31:return n.finish(24);case 32:case"end":return n.stop()}},n,this,[[9,20,24,32],[25,,27,31]])})},{key:"getAttributes",value:regeneratorRuntime.mark(function r(){return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.delegateYield(this._attrs.entries(),"t0",1);case 1:if(!(this._classes.size>0)){t.next=4;break}return t.next=4,["class",this.getAttribute("class")];case 4:if(!(this._styles.size>0)){t.next=7;break}return t.next=7,["style",this.getAttribute("style")];case 7:case"end":return t.stop()}},r,this)})},{key:"getAttribute",value:function(t){if("class"!=t){if("style"!=t)return this._attrs.get(t);if(this._styles.size>0){var e="",n=!0,r=!1,i=void 0;try{for(var o,a=this._styles[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=ns(o.value,2),u=s[0],l=s[1];e+=u+":"+l+";"}}catch(c){r=!0,i=c}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}return e}}else if(this._classes.size>0)return[].concat(rs(this._classes)).join(" ")}},{key:"hasAttribute",value:function(t){return"class"==t?this._classes.size>0:"style"==t?this._styles.size>0:this._attrs.has(t)}},{key:"setAttribute",value:function(t,e){this._fireChange("attributes",this),"class"==t?et(this._classes,e):"style"==t?tt(this._styles,e):this._attrs.set(t,e)}},{key:"insertChildren",value:function(t,e){this._fireChange("children",this);var n=0;e=nt(e);var r=!0,i=!1,o=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.parent=this,this._children.splice(t,0,u),t++,n++}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return n}},{key:"removeAttribute",value:function(t){return this._fireChange("attributes",this),"class"==t?this._classes.size>0&&(this._classes.clear(),!0):"style"==t?this._styles.size>0&&(this._styles.clear(),!0):this._attrs["delete"](t)}},{key:"removeChildren",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?1:arguments[1];this._fireChange("children",this);for(var n=t;n0?new this(n,r):new this(r,n)}},{key:"createIn",value:function(t){return this.createFromParentsAndOffsets(t,0,t,t.childCount)}},{key:"createOn",value:function(t){return this.createFromPositionAndShift(Bu.createBefore(t),1)}}]),t}(),zu=function(){function t(){var e=this;Qa(this,t),this._modelToViewMapping=new WeakMap,this._viewToModelMapping=new WeakMap,this._viewToModelLengthCallbacks=new Map,this.on("modelToViewPosition",function(t,n){var r=e._modelToViewMapping.get(n.modelPosition.parent);n.viewPosition=e._findPositionIn(r,n.modelPosition.offset)},{priority:"lowest"}),this.on("viewToModelPosition",function(t,n){for(var r=n.viewPosition.parent,i=e._viewToModelMapping.get(r);!i;)r=r.parent,i=e._viewToModelMapping.get(r);var o=e._toModelOffset(n.viewPosition.parent,n.viewPosition.offset,r);n.modelPosition=Cu.createFromParentAndOffset(i,o)},{priority:"lowest"})}return Xa(t,[{key:"bindElements",value:function(t,e){this._modelToViewMapping.set(t,e),this._viewToModelMapping.set(e,t)}},{key:"unbindViewElement",value:function(t){var e=this.toModelElement(t);this._unbindElements(e,t)}},{key:"unbindModelElement",value:function(t){var e=this.toViewElement(t);this._unbindElements(t,e)}},{key:"clearBindings",value:function(){this._modelToViewMapping=new WeakMap,this._viewToModelMapping=new WeakMap}},{key:"toModelElement",value:function(t){return this._viewToModelMapping.get(t)}},{key:"toViewElement",value:function(t){return this._modelToViewMapping.get(t)}},{key:"toModelRange",value:function(t){return new Ru(this.toModelPosition(t.start),this.toModelPosition(t.end))}},{key:"toViewRange",value:function(t){return new Lu(this.toViewPosition(t.start),this.toViewPosition(t.end))}},{key:"toModelPosition",value:function(t){var e={viewPosition:t,modelPosition:null,mapper:this};return this.fire("viewToModelPosition",e),e.modelPosition}},{key:"toViewPosition",value:function(t){var e={viewPosition:null,modelPosition:t,mapper:this};return this.fire("modelToViewPosition",e),e.viewPosition}},{key:"registerViewToModelLength",value:function(t,e){this._viewToModelLengthCallbacks.set(t,e)}},{key:"_toModelOffset",value:function(t,e,n){if(n!=t){var r=this._toModelOffset(t.parent,t.index,n),i=this._toModelOffset(t,e,t);return r+i}if(t instanceof Vu)return e;for(var o=0,a=0;a1)return null;t=t.parent}return!t||t.childCount>1?null:0}}]),e}(Nu);Ku.DEFAULT_PRIORITY=$u;var Wu=function(){function t(e){Qa(this,t),this._children=[],e&&this.insertChildren(0,e)}return Xa(t,[{key:Symbol.iterator,value:function(){return this._children[Symbol.iterator]()}},{key:"getAncestors",value:function(){return[]}},{key:"appendChildren",value:function(t){return this.insertChildren(this.childCount,t)}},{key:"getChild",value:function(t){return this._children[t]}},{key:"getChildIndex",value:function(t){return this._children.indexOf(t)}},{key:"getChildren",value:function(){return this._children[Symbol.iterator]()}},{key:"insertChildren",value:function(t,e){this._fireChange("children",this);var n=0;e=rt(e);var r=!0,i=!1,o=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.parent=this,this._children.splice(t,0,u),t++,n++}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return n}},{key:"removeChildren",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?1:arguments[1];this._fireChange("children",this);for(var n=t;nt.maxOffset)throw new ms("move-operation-nodes-do-not-exist: The nodes which should be moved do not exist.");
-if(t===e&&n=n&&this.targetPosition.path[i]0?this.operations[0].baseVersion:null},set:function(t){var e=!0,n=!1,r=void 0;try{for(var i,o=this.operations[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;a.baseVersion=t++}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}}},{key:"_reverseDeltaClass",get:function(){return t}}],[{key:"className",get:function(){return"engine.model.delta.Delta"}},{key:"_priority",get:function(){return 0}}]),t}();sf.register(uf);var lf=function(){function t(e){var n=arguments.length<=1||void 0===arguments[1]?"default":arguments[1];Qa(this,t),this.document=e,this.deltas=[],this.type=n}return Xa(t,[{key:"addDelta",value:function(t){return t.batch=this,this.deltas.push(t),t}},{key:"getOperations",value:regeneratorRuntime.mark(function e(){var t,n,r,i,o,a;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:t=!0,n=!1,r=void 0,e.prev=3,i=this.deltas[Symbol.iterator]();case 5:if(t=(o=i.next()).done){e.next=11;break}return a=o.value,e.delegateYield(a.operations,"t0",8);case 8:t=!0,e.next=5;break;case 11:e.next=17;break;case 13:e.prev=13,e.t1=e["catch"](3),n=!0,r=e.t1;case 17:e.prev=17,e.prev=18,!t&&i["return"]&&i["return"]();case 20:if(e.prev=20,!n){e.next=23;break}throw r;case 23:return e.finish(20);case 24:return e.finish(17);case 25:case"end":return e.stop()}},e,this,[[3,13,17,25],[18,,20,24]])})},{key:"baseVersion",get:function(){return this.deltas.length>0?this.deltas[0].baseVersion:null}}]),t}(),cf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"key",get:function(){return this.operations[0]?this.operations[0].key:null}},{key:"value",get:function(){return this.operations[0]?this.operations[0].newValue:null}},{key:"range",get:function(){if(this._range)return this._range;var t=this.operations[0],e=this.operations[this.operations.length-1];return t?(this._range=new Ru(t.range.start,e.range.end),this._range):null}},{key:"_reverseDeltaClass",get:function(){return e}}],[{key:"className",get:function(){return"engine.model.delta.AttributeDelta"}},{key:"_priority",get:function(){return 20}}]),e}(uf),ff=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,null,[{key:"className",get:function(){return"engine.model.delta.RootAttributeDelta"}}]),e}(uf);ln("setAttribute",function(t,e,n){return cn(this,e,n,t),this}),ln("removeAttribute",function(t,e){return cn(this,e,null,t),this}),sf.register(cf),sf.register(ff);var hf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"howMany",get:function(){return this._moveOperation?this._moveOperation.howMany:null}},{key:"sourcePosition",get:function(){return this._moveOperation?this._moveOperation.sourcePosition:null}},{key:"targetPosition",get:function(){return this._moveOperation?this._moveOperation.targetPosition:null}},{key:"_moveOperation",get:function(){return this.operations[0]||null}},{key:"_reverseDeltaClass",get:function(){return e}}],[{key:"className",get:function(){return"engine.model.delta.MoveDelta"}},{key:"_priority",get:function(){return 20}}]),e}(uf);ln("move",function(t,e){var n=new hf;if(this.addDelta(n),t instanceof Ru){if(!t.isFlat)throw new ms("batch-move-range-not-flat: Range to move is not flat.");dn(this,n,t.start,t.end.offset-t.start.offset,e)}else dn(this,n,Cu.createBefore(t),1,e);return this}),sf.register(hf);var df=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,null,[{key:"className",get:function(){return"engine.model.delta.RemoveDelta"}}]),e}(hf);ln("remove",function(t){var e=new df;if(this.addDelta(e),t instanceof Ru){var n=t.getMinimalFlatRanges().reverse(),r=!0,i=!1,o=void 0;try{for(var a,s=n[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;vn(this,e,u.start,u.end.offset-u.start.offset)}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}}else vn(this,e,Cu.createBefore(t),1);return this}),sf.register(df);var vf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"position",get:function(){return this._insertOperation?this._insertOperation.position:null}},{key:"nodes",get:function(){return this._insertOperation?this._insertOperation.nodes:null}},{key:"_insertOperation",get:function(){return this.operations[0]||null}},{key:"_reverseDeltaClass",get:function(){return df}}],[{key:"className",get:function(){return"engine.model.delta.InsertDelta"}},{key:"_priority",get:function(){return 20}}]),e}(uf);ln("insert",function(t,e){var n=new vf,r=new tf(t,e,this.document.version);return this.addDelta(n),n.addOperation(r),this.document.applyOperation(r),this}),sf.register(vf);var yf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"getReversed",value:function(){var t=Za(Object.getPrototypeOf(e.prototype),"getReversed",this).call(this);return t.operations.length>0&&(t.operations[0].isSticky=!0),t}},{key:"position",get:function(){return this._moveOperation?this._moveOperation.sourcePosition:null}},{key:"_cloneOperation",get:function(){return this.operations[0]||null}},{key:"_moveOperation",get:function(){return this.operations[1]||null}},{key:"_reverseDeltaClass",get:function(){return mf}}],[{key:"className",get:function(){return"engine.model.delta.SplitDelta"}},{key:"_priority",get:function(){return 5}}]),e}(uf);ln("split",function(t){var e=new yf;this.addDelta(e);var n=t.parent;if(!n.parent)throw new ms("batch-split-root: Root element can not be split.");var r=new Su(n.name,n.getAttributes()),i=new tf(Cu.createAfter(n),r,this.document.version);e.addOperation(i),this.document.applyOperation(i);var o=new Qc(t,n.maxOffset-t.offset,Cu.createFromParentAndOffset(r,0),this.document.version);return o.isSticky=!0,e.addOperation(o),this.document.applyOperation(o),this}),sf.register(yf);var mf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"getReversed",value:function(){var t=Za(Object.getPrototypeOf(e.prototype),"getReversed",this).call(this);return t.operations.length>0&&(t.operations[1].isSticky=!1),t}},{key:"position",get:function(){return this._removeOperation?this._removeOperation.sourcePosition:null}},{key:"_removeOperation",get:function(){return this.operations[1]||null}},{key:"_reverseDeltaClass",get:function(){return yf}}],[{key:"className",get:function(){return"engine.model.delta.MergeDelta"}}]),e}(uf);ln("merge",function(t){var e=new mf;this.addDelta(e);var n=t.nodeBefore,r=t.nodeAfter;if(!(n instanceof Su))throw new ms("batch-merge-no-element-before: Node before merge position must be an element.");if(!(r instanceof Su))throw new ms("batch-merge-no-element-after: Node after merge position must be an element.");var i=Cu.createFromParentAndOffset(r,0),o=Cu.createFromParentAndOffset(n,n.maxOffset),a=new Qc(i,r.maxOffset,o,this.document.version);a.isSticky=!0,e.addOperation(a),this.document.applyOperation(a);var s=new Zc(t,1,this.document.version);return e.addOperation(s),this.document.applyOperation(s),this}),sf.register(mf);var pf=function(t){function e(t,n,r,i){Qa(this,e);var o=es(this,Object.getPrototypeOf(e).call(this,i));return o.position=t,o.oldName=n,o.newName=r,o}return ts(e,t),Xa(e,[{key:"clone",value:function(){return new e(Cu.createFromPosition(this.position),this.oldName,this.newName,this.baseVersion)}},{key:"getReversed",value:function(){return new e(Cu.createFromPosition(this.position),this.newName,this.oldName,this.baseVersion+1)}},{key:"_execute",value:function(){var t=this.position.nodeAfter;if(!(t instanceof Su))throw new ms("rename-operation-wrong-position: Given position is invalid or node after it is not an instance of Element.");if(t.name!==this.oldName)throw new ms("rename-operation-wrong-name: Element to change has different name than operation's old name.");return t.name=this.newName,{element:t,oldName:this.oldName}}},{key:"type",get:function(){return"rename"}}],[{key:"fromJSON",value:function(t,n){return new e(Cu.fromJSON(t.position,n),t.oldName,t.newName,t.baseVersion)}},{key:"className",get:function(){return"engine.model.operation.RenameOperation"}}]),e}(Gc),gf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"_reverseDeltaClass",get:function(){return e}}],[{key:"className",get:function(){return"engine.model.delta.RenameDelta"}}]),e}(uf);ln("rename",function(t,e){if(!(t instanceof Su))throw new ms("batch-rename-not-element-instance: Trying to rename an object which is not an instance of Element.");var n=new gf;return this.addDelta(n),yn(this,n,new pf(Cu.createBefore(t),t.name,e,this.document.version)),this}),sf.register(gf);var bf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"range",get:function(){var t=this._moveOperation;return t?Ru.createFromPositionAndShift(t.sourcePosition,t.howMany):null}},{key:"howMany",get:function(){var t=this.range;return t?t.end.offset-t.start.offset:0}},{key:"_insertOperation",get:function(){return this.operations[0]||null}},{key:"_moveOperation",get:function(){return this.operations[1]||null}},{key:"_reverseDeltaClass",get:function(){return wf}}],[{key:"className",get:function(){return"engine.model.delta.WrapDelta"}},{key:"_priority",get:function(){return 10}}]),e}(uf);ln("wrap",function(t,e){if(!t.isFlat)throw new ms("batch-wrap-range-not-flat: Range to wrap is not flat.");var n=e instanceof Su?e:new Su(e);if(n.childCount>0)throw new ms("batch-wrap-element-not-empty: Element to wrap with is not empty.");if(null!==n.parent)throw new ms("batch-wrap-element-attached: Element to wrap with is already attached to tree model.");var r=new bf;this.addDelta(r);var i=new tf(t.end,n,this.document.version);r.addOperation(i),this.document.applyOperation(i);var o=Cu.createFromParentAndOffset(n,0),a=new Qc(t.start,t.end.offset-t.start.offset,o,this.document.version);return r.addOperation(a),this.document.applyOperation(a),this}),sf.register(bf);var wf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"position",get:function(){return this._moveOperation?this._moveOperation.targetPosition:null}},{key:"_moveOperation",get:function(){return this.operations[0]||null}},{key:"_reverseDeltaClass",get:function(){return bf}}],[{key:"className",get:function(){return"engine.model.delta.UnwrapDelta"}},{key:"_priority",get:function(){return 10}}]),e}(uf);ln("unwrap",function(t){if(null===t.parent)throw new ms("batch-unwrap-element-no-parent: Trying to unwrap an element that has no parent.");var e=new wf;this.addDelta(e);var n=Cu.createFromParentAndOffset(t,0),r=new Qc(n,t.maxOffset,Cu.createBefore(t),this.document.version);r.isSticky=!0,e.addOperation(r),this.document.applyOperation(r);var i=new Zc(Cu.createBefore(t),1,this.document.version);return e.addOperation(i),this.document.applyOperation(i),this}),sf.register(wf);var kf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,null,[{key:"className",get:function(){return"engine.model.delta.WeakInsertDelta"}}]),e}(vf);ln("weakInsert",function(t,e){var n=new kf;this.addDelta(n),e=Kt(e);var r=!0,i=!1,o=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.setAttributesTo(this.document.selection.getAttributes())}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}var c=new tf(t,e,this.document.version);return n.addOperation(c),this.document.applyOperation(c),this}),sf.register(kf);var _f="__lodash_hash_undefined__";gn.prototype.add=gn.prototype.push=mn,gn.prototype.has=pn;var xf=1,Of=2,Pf=1,Sf=2,Af="[object Boolean]",Cf="[object Date]",Tf="[object Error]",Ef="[object Map]",Rf="[object Number]",jf="[object RegExp]",Vf="[object Set]",Ff="[object String]",Nf="[object Symbol]",Mf="[object ArrayBuffer]",If="[object DataView]",Bf=Jl?Jl.prototype:void 0,Df=Bf?Bf.valueOf:void 0,Lf=2,zf="[object Arguments]",qf="[object Array]",Hf="[object Boolean]",$f="[object Date]",Kf="[object Error]",Wf="[object Function]",Jf="[object Map]",Uf="[object Number]",Gf="[object Object]",Yf="[object RegExp]",Qf="[object Set]",Xf="[object String]",Zf="[object WeakMap]",th="[object ArrayBuffer]",eh="[object DataView]",nh="[object Float32Array]",rh="[object Float64Array]",ih="[object Int8Array]",oh="[object Int16Array]",ah="[object Int32Array]",sh="[object Uint8Array]",uh="[object Uint8ClampedArray]",lh="[object Uint16Array]",ch="[object Uint32Array]",fh={};fh[nh]=fh[rh]=fh[ih]=fh[oh]=fh[ah]=fh[sh]=fh[uh]=fh[lh]=fh[ch]=!0,fh[zf]=fh[qf]=fh[th]=fh[Hf]=fh[eh]=fh[$f]=fh[Kf]=fh[Wf]=fh[Jf]=fh[Uf]=fh[Gf]=fh[Yf]=fh[Qf]=fh[Xf]=fh[Zf]=!1;var hh=Object.prototype,dh=hh.toString,vh=2,yh="[object Arguments]",mh="[object Array]",ph="[object Object]",gh=Object.prototype,bh=gh.hasOwnProperty,wh={InsertOperation:{InsertOperation:function(t,e,n){var r=t.clone();return r.position=r.position._getTransformedByInsertion(e.position,e.nodes.maxOffset,!n),[r]},AttributeOperation:Cn,RootAttributeOperation:Cn,RenameOperation:Cn,MoveOperation:function(t,e,n){var r=t.clone();return r.position=t.position._getTransformedByMove(e.sourcePosition,e.targetPosition,e.howMany,!n,e.isSticky),[r]}},AttributeOperation:{InsertOperation:function(t,e){var n=t.range._getTransformedByInsertion(e.position,e.nodes.maxOffset,!0,!1);return n.reverse().map(function(e){return new Yc(e,t.key,t.oldValue,t.newValue,t.baseVersion)})},AttributeOperation:function(t,e,n){if(t.key===e.key){var r=t.range.getDifference(e.range).map(function(e){return new Yc(e,t.key,t.oldValue,t.newValue,t.baseVersion)});if(n&&!Sn(t.newValue,e.newValue)){var i=t.range.getIntersection(e.range);null!==i&&r.push(new Yc(i,e.key,e.oldValue,t.newValue,t.baseVersion))}return 0===r.length&&r.push(new ef(t.baseVersion)),r}return[t.clone()]},RootAttributeOperation:Cn,RenameOperation:Cn,MoveOperation:function(t,e){var n=Ru.createFromPositionAndShift(e.sourcePosition,e.howMany),r=[];e instanceof Zc&&e._needsHolderElement&&t.range.root==e.targetPosition.root&&t.range.start.path[0]>=e._holderElementOffset&&(t=t.clone(),t.range.start.path[0]++,t.range.end.path[0]++);var i=Rn(t.range.getDifference(n)),o=t.range.getIntersection(n);return null!==i&&(i.start=i.start._getTransformedByDeletion(e.sourcePosition,e.howMany),i.end=i.end._getTransformedByDeletion(e.sourcePosition,e.howMany),r=i._getTransformedByInsertion(e.movedRangeStart,e.howMany,!0,!1).reverse()),null!==o&&(o.start=o.start._getCombined(e.sourcePosition,e.movedRangeStart),o.end=o.end._getCombined(e.sourcePosition,e.movedRangeStart),r.push(o)),r.map(function(e){return new Yc(e,t.key,t.oldValue,t.newValue,t.baseVersion)})}},RootAttributeOperation:{InsertOperation:Cn,AttributeOperation:Cn,RootAttributeOperation:function(t,e,n){return t.root!==e.root||t.key!==e.key||(t.newValue===e.newValue||n)&&t.newValue!==e.newValue?[t.clone()]:[new ef(t.baseVersion)]},RenameOperation:Cn,MoveOperation:Cn},RenameOperation:{InsertOperation:function(t,e){var n=t.clone();return n.position=n.position._getTransformedByInsertion(e.position,e.nodes.maxOffset,!0),[n]},AttributeOperation:Cn,RootAttributeOperation:Cn,RenameOperation:function(t,e,n){var r=t.clone();return t.position.isEqual(e.position)&&!n?[new ef(t.baseVersion)]:[r]},MoveOperation:function(t,e){var n=t.clone(),r=n.position.isEqual(e.sourcePosition);return n.position=n.position._getTransformedByMove(e.sourcePosition,e.targetPosition,e.howMany,!0,r),[n]}},MoveOperation:{InsertOperation:function(t,e,n){var r=Ru.createFromPositionAndShift(t.sourcePosition,t.howMany);r=r._getTransformedByInsertion(e.position,e.nodes.maxOffset,!1,t.isSticky)[0];var i=new t.constructor(r.start,r.end.offset-r.start.offset,t instanceof Zc?t.baseVersion:t.targetPosition._getTransformedByInsertion(e.position,e.nodes.maxOffset,!n),t instanceof Zc?void 0:t.baseVersion);return i.isSticky=t.isSticky,[i]},AttributeOperation:Cn,RootAttributeOperation:Cn,RenameOperation:Cn,MoveOperation:function(t,e,n){if(En(t,e)&&En(e,t))return[e.getReversed()];if(t instanceof Zc&&e instanceof Zc&&e._needsHolderElement){var r=t.targetPosition.path[0],i=e.targetPosition.path[0];(r>i||r==i&&n)&&(t=t.clone(),t.targetPosition.path[0]++)}t instanceof Zc&&!(e instanceof Zc)?n=!0:!(t instanceof Zc)&&e instanceof Zc&&(n=!1);var o=Ru.createFromPositionAndShift(t.sourcePosition,t.howMany),a=Ru.createFromPositionAndShift(e.sourcePosition,e.howMany),s=[],u=Rn(o.getDifference(a));u&&(u.start=u.start._getTransformedByMove(e.sourcePosition,e.targetPosition,e.howMany,!t.isSticky,!1),u.end=u.end._getTransformedByMove(e.sourcePosition,e.targetPosition,e.howMany,t.isSticky,!1),s.push(u));var l=o.getIntersection(a),c=X(t.sourcePosition.getParentPath(),e.sourcePosition.getParentPath()),f=o.containsPosition(e.targetPosition)||o.start.isEqual(e.targetPosition)&&t.isSticky||o.end.isEqual(e.targetPosition)&&t.isSticky,h=a.containsRange(o)&&(a.containsPosition(t.targetPosition)||a.start.isEqual(t.targetPosition)||a.end.isEqual(t.targetPosition));if(null!==l&&("extension"===c||"same"===c&&n||h)&&!f&&(l.start=l.start._getCombined(e.sourcePosition,e.movedRangeStart),l.end=l.end._getCombined(e.sourcePosition,e.movedRangeStart),u&&u.start.isBefore(l.start)?s.push(l):s.unshift(l)),0===s.length)return[new ef(t.baseVersion)];var d=t.targetPosition._getTransformedByMove(e.sourcePosition,e.targetPosition,e.howMany,!n,e.isSticky||h);return s.reverse().map(function(e){var n=new t.constructor(e.start,e.end.offset-e.start.offset,t instanceof Zc?t.baseVersion:d,t instanceof Zc?void 0:t.baseVersion);return n.isSticky=t.isSticky,n._holderElementOffset=t._holderElementOffset,n})}}},kh=Math.ceil,_h=Math.max,xh=200,Oh=P(function(t,e){return j(t)?Kn(t,Mn(e,1,j,!0)):[]}),Ph=1,Sh=2,Ah="[object Map]",Ch="[object Set]",Th=Yn(Ve),Eh="Expected a function";tr.Cache=Ae;var Rh=1/0,jh=Jl?Jl.prototype:void 0,Vh=jh?jh.toString:void 0,Fh=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]/g,Nh=/\\(\\)?/g,Mh=tr(function(t){var e=[];return nr(t).replace(Fh,function(t,n,r,i){e.push(r?i.replace(Nh,"$1"):n||t)}),e}),Ih=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Bh=/^\w*$/,Dh=1/0,Lh=1,zh=2,qh=P(function(t,e){var n=Q(e);return j(n)&&(n=void 0),j(t)?Kn(t,Mn(e,1,j,!0),yr(n)):[]}),Hh=P(function(t,e){var n=Q(e);return j(n)&&(n=void 0),j(t)?Kn(t,Mn(e,1,j,!0),void 0,n):[]}),$h=4294967295,Kh=1/0,Wh=Math.max,Jh=Math.min,Uh=P(function(t){var e=qn(t,Mr);return e.length&&e[0]===t[0]?Nr(e):[]}),Gh=P(function(t){var e=Q(t),n=qn(t,Mr);return e===Q(n)?e=void 0:n.pop(),n.length&&n[0]===t[0]?Nr(n,yr(e)):[]}),Yh=P(function(t){var e=Q(t),n=qn(t,Mr);return e===Q(n)?e=void 0:n.pop(),n.length&&n[0]===t[0]?Nr(n,void 0,e):[]}),Qh=Array.prototype,Xh=Qh.join,Zh=Math.max,td=Math.min,ed=Array.prototype,nd=ed.splice,rd=P(Hr),id=Array.prototype,od=id.splice,ad=P(function(t,e){e=Mn(e,1);var n=t?t.length:0,r=Wr(t,e);return Ur(t,qn(e,function(t){return g(t,n)?+t:t}).sort(Gr)),r}),sd=Array.prototype,ud=sd.reverse,ld=4294967295,cd=ld-1,fd=Math.floor,hd=Math.min,dd=4294967295,vd=dd>>>1,yd=1/0,md=Al&&1/Xe(new Al([,-0]))[1]==yd?function(t){return new Al(t)}:yi,pd=200,gd=P(function(t){return mi(Mn(t,1,j,!0))}),bd=P(function(t){var e=Q(t);return j(e)&&(e=void 0),mi(Mn(t,1,j,!0),yr(e))}),wd=P(function(t){var e=Q(t);return j(e)&&(e=void 0),mi(Mn(t,1,j,!0),void 0,e)}),kd=Math.max,_d=P(function(t,e){return j(t)?Kn(t,e):[]}),xd=P(function(t){return xi(wi(t,j))}),Od=P(function(t){var e=Q(t);return j(e)&&(e=void 0),xi(wi(t,j),yr(e))}),Pd=P(function(t){var e=Q(t);return j(e)&&(e=void 0),xi(wi(t,j),void 0,e)}),Sd=P(ki),Ad=P(function(t){var e=t.length,n=e>1?t[e-1]:void 0;return n="function"==typeof n?(t.pop(),n):void 0,_i(t,n)}),Cd={chunk:Vn,compact:Fn,concat:In,difference:Oh,differenceBy:qh,differenceWith:Hh,drop:mr,dropRight:pr,dropRightWhile:br,dropWhile:wr,fill:Or,findIndex:Sr,findLastIndex:Ar,first:Cr,flatten:Tr,flattenDeep:Er,flattenDepth:Rr,fromPairs:jr,head:Cr,indexOf:Vr,initial:Fr,intersection:Uh,intersectionBy:Gh,intersectionWith:Yh,join:Ir,last:Q,lastIndexOf:Br,nth:Lr,pull:rd,pullAll:Hr,pullAllBy:$r,pullAllWith:Kr,pullAt:ad,remove:Yr,reverse:Qr,slice:Xr,sortedIndex:ei,sortedIndexBy:ni,sortedIndexOf:ri,sortedLastIndex:ii,sortedLastIndexBy:oi,sortedLastIndexOf:ai,sortedUniq:ui,sortedUniqBy:li,tail:ci,take:fi,takeRight:hi,takeRightWhile:di,takeWhile:vi,union:gd,unionBy:bd,unionWith:wd,uniq:pi,uniqBy:gi,uniqWith:bi,unzip:ki,unzipWith:_i,without:_d,xor:xd,xorBy:Od,xorWith:Pd,zip:Sd,zipObject:Pi,zipObjectDeep:Ai,zipWith:Ad},Td=new Map;Ri(cf,kf,function(t,e,n){var r=Ei(t,e,n);return t.range.containsPosition(e.position)&&r.push(Fi(e,t)),r}),Ri(vf,mf,function(t,e,n){return t.position.isEqual(e.position)?[e.getReversed(),t.clone()]:Ei(t,e,n)}),Ri(hf,mf,function(t,e,n){var r=t.sourcePosition.root==e.position.root&&"same"===X(t.sourcePosition.getParentPath(),e.position.getParentPath()),i=t.sourcePosition.offset<=e.position.offset&&t.sourcePosition.offset+t.howMany>e.position.offset;return r&&i?[e.getReversed(),t.clone()]:Ei(t,e,n)}),Ri(mf,vf,function(t,e,n){return t.position.isEqual(e.position)?[Ni()]:Ei(t,e,n)}),Ri(mf,hf,function(t,e,n){var r=t.position.root==e.sourcePosition.root&&"same"===X(t.position.getParentPath(),e.sourcePosition.getParentPath()),i=e.sourcePosition.offset<=t.position.offset&&e.sourcePosition.offset+e.howMany>t.position.offset;return r&&i?[Ni()]:Ei(t,e,n)}),Ri(yf,yf,function(t,e,n){var r=t.position.getParentPath(),i=e.position.getParentPath();if("same"==X(r,i)){if(t.position.offset==e.position.offset)return[Ni()];if(t.position.offsete._cloneOperation.sourcePosition.offset&&o._cloneOperation.sourcePosition.offset--,[o]}var a=t.clone();return a._cloneOperation.position.offset++,a._moveOperation.sourcePosition.path[a._moveOperation.sourcePosition.path.length-2]++,a._moveOperation.targetPosition.path[a._moveOperation.targetPosition.path.length-2]++,a._moveOperation.sourcePosition.offset=t.position.offset-e.position.offset,t._cloneOperation instanceof Xc&&e._cloneOperation instanceof Xc&&t._cloneOperation.sourcePosition.offset>e._cloneOperation.sourcePosition.offset&&a._cloneOperation.sourcePosition.offset--,[a]}return Ei(t,e,n)}),Ri(yf,wf,function(t,e,n){return"same"===X(e.position.path,t.position.getParentPath())?[Ni()]:Ei(t,e,n)}),Ri(yf,bf,function(t,e,n){var r="same"===X(t.position.getParentPath(),e.range.start.getParentPath()),i=e.range.start.offset=t.position.offset;if(r&&i)return[Ni()];if("same"===X(t.position.getParentPath(),e.range.end.getShiftedBy(-1).path)){var o=t.clone(),a=Cu.createFromPosition(e.range.start);a.path.push(e.howMany-1);var s=a.getShiftedBy(1);o._cloneOperation.position=s;var u=Cu.createFromPosition(a);u.path.push(t.position.offset),o._moveOperation.sourcePosition=u;var l=Cu.createFromPosition(s);return l.path.push(0),o._moveOperation.targetPosition=l,[o]}return Ei(t,e,n)}),Ri(wf,yf,function(t,e,n){if("same"===X(t.position.path,e.position.getParentPath())){var r=[e.getReversed(),t.clone()];return r[1].operations[1].targetPosition.path[0]++,r}return Ei(t,e,n)}),Ri(kf,cf,function(t,e,n){var r=Ei(t,e,n);return e.range.containsPosition(t.position)&&r.push(Fi(t,e)),r}),Ri(bf,yf,function(t,e,n){var r="same"===X(t.range.start.getParentPath(),e.position.getParentPath()),i=t.range.start.offset=e.position.offset;if(r&&i)return[e.getReversed(),t.clone()];if("same"===X(e.position.getParentPath(),t.range.end.getShiftedBy(-1).path)){var o=t.clone();return o._insertOperation.position.offset++,o._moveOperation.howMany++,o._moveOperation.targetPosition.path[o._moveOperation.targetPosition.path.length-2]++,
-[o]}return Ei(t,e,n)});var Ed=function(){function t(){Qa(this,t),this._deltas=[],this._historyPoints=new Map}return Xa(t,[{key:"addDelta",value:function(t){if(t.operations.length>0&&!this._historyPoints.has(t.baseVersion)){var e=this._deltas.length;this._deltas[e]=t,this._historyPoints.set(t.baseVersion,e)}}},{key:"getDeltas",value:regeneratorRuntime.mark(function e(){var t,n,r=arguments.length<=0||void 0===arguments[0]?0:arguments[0],i=arguments.length<=1||void 0===arguments[1]?Number.POSITIVE_INFINITY:arguments[1];return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(0!==this._deltas.length){e.next=2;break}return e.abrupt("return");case 2:if(t=this._getIndex(r),t!=-1){e.next=5;break}return e.abrupt("return");case 5:if(!(t=i)){e.next=9;break}return e.abrupt("break",13);case 9:return e.next=11,n;case 11:e.next=5;break;case 13:case"end":return e.stop()}},e,this)})},{key:"getDelta",value:function(t){var e=this._historyPoints.get(t);if(void 0===e)return null;var n=[];for(e;et&&this._historyPoints.set(p,this._historyPoints.get(p)+f)}}catch(c){d=!0,v=c}finally{try{!h&&m["return"]&&m["return"]()}finally{if(d)throw v}}}}},{key:"_getIndex",value:function(t){var e=this._historyPoints.get(t);if(void 0===e){var n=this._deltas[this._deltas.length-1],r=n.baseVersion+n.operations.length;if(t<0||t>=r)return-1;throw new ms("model-history-wrong-version: Given base version points to the middle of a delta.")}return e}}]),t}(),Rd=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n));return Mi.call(r),r}return ts(e,t),Xa(e,[{key:"detach",value:function(){this.stopListening()}}]),e}(Ru);K(Rd,ss);var jd=function(){function t(){Qa(this,t),this._lastRangeBackward=!1,this._ranges=[],this._attrs=new Map}return Xa(t,[{key:"isEqual",value:function(t){var e=this.rangeCount;if(e!=t.rangeCount)return!1;for(var n=0;n0&&(this._removeAllRanges(),this.fire("change:range",{directChange:!0}))}},{key:"setRanges",value:function(t){var e=this,n=!(arguments.length<=1||void 0===arguments[1])&&arguments[1];t=Array.from(t);var r=t.some(function(t){if(!(t instanceof Ru))throw new ms("model-selection-added-not-range: Trying to add an object that is not an instance of Range.");return e._ranges.every(function(e){return!e.isEqual(t)})});if(t.length!==this._ranges.length||r){this._removeAllRanges();var i=!0,o=!1,a=void 0;try{for(var s,u=t[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;this._pushRange(l)}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}this._lastRangeBackward=!!n,this.fire("change:range",{directChange:!0})}}},{key:"setTo",value:function(t){this.setRanges(t.getRanges(),t.isBackward)}},{key:"collapse",value:function(t,e){var n=Cu.createAt(t,e),r=new Ru(n,n);this.setRanges([r])}},{key:"collapseToStart",value:function(){var t=this.getFirstPosition();null!==t&&this.setRanges([new Ru(t,t)])}},{key:"collapseToEnd",value:function(){var t=this.getLastPosition();null!==t&&this.setRanges([new Ru(t,t)])}},{key:"setFocus",value:function(t,e){if(null===this.anchor)throw new ms("model-selection-setFocus-no-ranges: Cannot set selection focus if there are no ranges in selection.");var n=Cu.createAt(t,e);if("same"!=n.compareWith(this.focus)){var r=this.anchor;this._ranges.length&&this._popRange(),"before"==n.compareWith(r)?this.addRange(new Ru(n,r),!0):this.addRange(new Ru(r,n))}}},{key:"getAttribute",value:function(t){return this._attrs.get(t)}},{key:"getAttributes",value:function(){return this._attrs.entries()}},{key:"getAttributeKeys",value:function(){return this._attrs.keys()}},{key:"hasAttribute",value:function(t){return this._attrs.has(t)}},{key:"clearAttributes",value:function(){if(this._attrs.size>0){var t=Array.from(this._attrs.keys());this._attrs.clear(),this.fire("change:attribute",{attributeKeys:t,directChange:!0})}}},{key:"removeAttribute",value:function(t){this.hasAttribute(t)&&(this._attrs["delete"](t),this.fire("change:attribute",{attributeKeys:[t],directChange:!0}))}},{key:"setAttribute",value:function(t,e){this.getAttribute(t)!==e&&(this._attrs.set(t,e),this.fire("change:attribute",{attributeKeys:[t],directChange:!0}))}},{key:"setAttributesTo",value:function(t){if(t=J(t),!Bi(t,this._attrs)){var e=new Set(Array.from(t.keys()).concat(Array.from(this._attrs.keys()))),n=!0,r=!1,i=void 0;try{for(var o,a=t[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=ns(o.value,2),u=s[0],l=s[1];this._attrs.get(u)===l&&e["delete"](u)}}catch(c){r=!0,i=c}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}this._attrs=t,this.fire("change:attribute",{attributeKeys:Array.from(e),directChange:!0})}}},{key:"_pushRange",value:function(t){if(!(t instanceof Ru))throw new ms("model-selection-added-not-range: Trying to add an object that is not an instance of Range.");this._checkRange(t),this._ranges.push(Ru.createFromRange(t))}},{key:"_checkRange",value:function(t){for(var e=0;e0;)this._popRange()}},{key:"anchor",get:function(){if(this._ranges.length>0){var t=this._ranges[this._ranges.length-1];return this._lastRangeBackward?t.end:t.start}return null}},{key:"focus",get:function(){if(this._ranges.length>0){var t=this._ranges[this._ranges.length-1];return this._lastRangeBackward?t.start:t.end}return null}},{key:"isCollapsed",get:function(){var t=this._ranges.length;return 1===t&&this._ranges[0].isCollapsed}},{key:"rangeCount",get:function(){return this._ranges.length}},{key:"isBackward",get:function(){return!this.isCollapsed&&this._lastRangeBackward}}],[{key:"createFromSelection",value:function(t){var e=new this;return e.setTo(t),e}}]),t}();K(jd,ss);var Vd="selection:",Fd=new Set(["addAttribute","removeAttribute","changeAttribute","addRootAttribute","removeRootAttribute","changeRootAttribute"]),Nd=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this));return n._document=t,n._attributePriority=new Map,n.listenTo(n._document,"change",function(t,e){Fd.has(e)&&n._updateAttributes(!1)}),n}return ts(e,t),Xa(e,[{key:"destroy",value:function(){for(var t=0;t0){var n=Array.from(e);this.fire("change:attribute",{attributeKeys:n,directChange:!0})}}},{key:"clearAttributes",value:function(){this.setAttributesTo([])}},{key:"refreshAttributes",value:function(){this._updateAttributes(!0)}},{key:"_popRange",value:function(){this._ranges.pop().detach()}},{key:"_pushRange",value:function(t){var e=this._prepareRange(t);e&&this._ranges.push(e)}},{key:"_prepareRange",value:function(t){var e=this;if(!(t instanceof Ru))throw new ms("model-selection-added-not-range: Trying to add an object that is not an instance of Range.");if(t.root==this._document.graveyard)return void wu.warn("model-selection-range-in-graveyard: Trying to add a Range that is in the graveyard root. Range rejected.");this._checkRange(t);var n=Rd.createFromRange(t);return this.listenTo(n,"change",function(t,r){n.root==e._document.graveyard&&e._fixGraveyardSelection(n,r),e.fire("change:range",{directChange:!1})}),n}},{key:"_updateAttributes",value:function(t){var e=J(this._getSurroundingAttributes()),n=J(this.getAttributes());if(t)this._attributePriority=new Map,this._attrs=new Map;else{var r=!0,i=!1,o=void 0;try{for(var a,s=this._attributePriority[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=ns(a.value,2),l=u[0],c=u[1];"low"==c&&(this._attrs["delete"](l),this._attributePriority["delete"](l))}}catch(f){i=!0,o=f}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}}this._setAttributesTo(e,!1);var h=[],d=!0,v=!1,y=void 0;try{for(var m,p=this.getAttributes()[Symbol.iterator]();!(d=(m=p.next()).done);d=!0){var g=ns(m.value,2),b=g[0],w=g[1];n.has(b)&&n.get(b)===w||h.push(b)}}catch(f){v=!0,y=f}finally{try{!d&&p["return"]&&p["return"]()}finally{if(v)throw y}}var k=!0,_=!1,x=void 0;try{for(var O,P=n[Symbol.iterator]();!(k=(O=P.next()).done);k=!0){var S=ns(O.value,1),A=S[0];this.hasAttribute(A)||h.push(A)}}catch(f){_=!0,x=f}finally{try{!k&&P["return"]&&P["return"]()}finally{if(_)throw x}}h.length>0&&this.fire("change:attribute",{attributeKeys:h,directChange:!1})}},{key:"_setAttribute",value:function(t,n){var r=arguments.length<=2||void 0===arguments[2]||arguments[2],i=r?"normal":"low";if("low"==i&&"normal"==this._attributePriority.get(t))return!1;var o=Za(Object.getPrototypeOf(e.prototype),"getAttribute",this).call(this,t);return o!==n&&(this._attrs.set(t,n),this._attributePriority.set(t,i),!0)}},{key:"_removeAttribute",value:function(t){var n=arguments.length<=1||void 0===arguments[1]||arguments[1],r=n?"normal":"low";return("low"!=r||"normal"!=this._attributePriority.get(t))&&(!!Za(Object.getPrototypeOf(e.prototype),"hasAttribute",this).call(this,t)&&(this._attrs["delete"](t),this._attributePriority.set(t,r),!0))}},{key:"_setAttributesTo",value:function(t){var e=arguments.length<=1||void 0===arguments[1]||arguments[1],n=new Set,r=!0,i=!1,o=void 0;try{for(var a,s=this.getAttributes()[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=ns(a.value,2),l=u[0],c=u[1];t.get(l)!==c&&this._removeAttribute(l,e)&&n.add(l)}}catch(f){i=!0,o=f}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}var h=!0,d=!1,v=void 0;try{for(var y,m=t[Symbol.iterator]();!(h=(y=m.next()).done);h=!0){var p=ns(y.value,2),g=p[0],b=p[1],w=this._setAttribute(g,b,e);w&&n.add(g)}}catch(f){d=!0,v=f}finally{try{!h&&m["return"]&&m["return"]()}finally{if(d)throw v}}return n}},{key:"_getStoredAttributes",value:regeneratorRuntime.mark(function r(){var t,e,n,i,o,a,s,u;return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:if(t=this.getFirstPosition().parent,!this.isCollapsed||0!==t.childCount){r.next=30;break}e=!0,n=!1,i=void 0,r.prev=5,o=t.getAttributeKeys()[Symbol.iterator]();case 7:if(e=(a=o.next()).done){r.next=16;break}if(s=a.value,0!==s.indexOf(Vd)){r.next=13;break}return u=s.substr(Vd.length),r.next=13,[u,t.getAttribute(s)];case 13:e=!0,r.next=7;break;case 16:r.next=22;break;case 18:r.prev=18,r.t0=r["catch"](5),n=!0,i=r.t0;case 22:r.prev=22,r.prev=23,!e&&o["return"]&&o["return"]();case 25:if(r.prev=25,!n){r.next=28;break}throw i;case 28:return r.finish(25);case 29:return r.finish(22);case 30:case"end":return r.stop()}},r,this,[[5,18,22,30],[23,,25,29]])})},{key:"_removeStoredAttribute",value:function(t){var n=e._getStoreAttributeKey(t);this._document.batch().removeAttribute(this.anchor.parent,n)}},{key:"_storeAttribute",value:function(t,n){var r=e._getStoreAttributeKey(t);this._document.batch().setAttribute(this.anchor.parent,r,n)}},{key:"_setStoredAttributesTo",value:function(t){var n=this.anchor.parent,r=this._document.batch(),i=!0,o=!1,a=void 0;try{for(var s,u=this._getStoredAttributes()[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=ns(s.value,1),c=l[0],f=e._getStoreAttributeKey(c);r.removeAttribute(n,f)}}catch(h){o=!0,a=h}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}var d=!0,v=!1,y=void 0;try{for(var m,p=t[Symbol.iterator]();!(d=(m=p.next()).done);d=!0){var g=ns(m.value,2),b=g[0],w=g[1],k=e._getStoreAttributeKey(b);r.setAttribute(n,k,w)}}catch(h){v=!0,y=h}finally{try{!d&&p["return"]&&p["return"]()}finally{if(v)throw y}}}},{key:"_getSurroundingAttributes",value:function(){var t=this.getFirstPosition(),e=null;if(this.isCollapsed){var n=t.textNode?t.textNode:t.nodeBefore,r=t.textNode?t.textNode:t.nodeAfter;if(e=Di(n),e||(e=Di(r)),!e)for(var i=n;i&&!e;)i=i.previousSibling,e=Di(i);if(!e)for(var o=r;o&&!e;)o=o.nextSibling,e=Di(o);e||(e=this._getStoredAttributes())}else{var a=this.getFirstRange(),s=!0,u=!1,l=void 0;try{for(var c,f=a[Symbol.iterator]();!(s=(c=f.next()).done);s=!0){var h=c.value;"text"==h.type&&null===e&&(e=h.item.getAttributes())}}catch(d){u=!0,l=d}finally{try{!s&&f["return"]&&f["return"]()}finally{if(u)throw l}}}return e}},{key:"_fixGraveyardSelection",value:function(t,e){var n=t.start.path,r=e.start.path.length-(n.length-2),i=e.start.path.slice(0,r);i[i.length-1]-=n[1];var o=new Cu(e.root,i),a=this._prepareRange(new Ru(o,o)),s=this._ranges.indexOf(t);this._ranges.splice(s,1,a),t.detach()}},{key:"isCollapsed",get:function(){var t=this._ranges.length;return 0===t||Za(Object.getPrototypeOf(e.prototype),"isCollapsed",this)}},{key:"anchor",get:function(){return Za(Object.getPrototypeOf(e.prototype),"anchor",this)||this._document._getDefaultRange().start}},{key:"focus",get:function(){return Za(Object.getPrototypeOf(e.prototype),"focus",this)||this._document._getDefaultRange().start}},{key:"rangeCount",get:function(){return this._ranges.length?this._ranges.length:1}}],[{key:"createFromSelection",value:function(t){var e=new this(t._document);return e.setTo(t),e}},{key:"_getStoreAttributeKey",value:function(t){return Vd+t}}]),e}(jd),Md=function(){function t(e){Qa(this,t),this._schema=e,this._allowed=[],this._disallowed=[],this._requiredAttributes=[]}return Xa(t,[{key:"allow",value:function(t,e){this._addPath("_allowed",t,e)}},{key:"disallow",value:function(t,e){this._addPath("_disallowed",t,e)}},{key:"requireAttributes",value:function(t){this._requiredAttributes.push(t)}},{key:"_addPath",value:function(t,e,n){e=e.slice(),ou(n)||(n=[n]);var r=!0,i=!1,o=void 0;try{for(var a,s=n[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;this[t].push({path:e,attribute:u})}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}}},{key:"_getPaths",value:function(t,e){var n="allow"===t?this._allowed:this._disallowed,r=[],i=!0,o=!1,a=void 0;try{for(var s,u=n[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;l.attribute===e&&r.push(l.path)}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}return r}},{key:"_checkRequiredAttributes",value:function(t){var e=!0,n=!0,r=!1,i=void 0;try{for(var o,a=this._requiredAttributes[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;e=!0;var u=!0,l=!1,c=void 0;try{for(var f,h=s[Symbol.iterator]();!(u=(f=h.next()).done);u=!0){var d=f.value;if(t.indexOf(d)==-1){e=!1;break}}}catch(v){l=!0,c=v}finally{try{!u&&h["return"]&&h["return"]()}finally{if(l)throw c}}if(e)break}}catch(v){r=!0,i=v}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}return e}},{key:"_hasMatchingPath",value:function(t,e,n){var r=this._getPaths(t,n),i=!0,o=!1,a=void 0;try{for(var s,u=r[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value,c=0,f=!0,h=!1,d=void 0;try{for(var v,y=e[Symbol.iterator]();!(f=(v=y.next()).done);f=!0){var m=v.value;if(this._schema.hasItem(m)){var p=this._schema._extensionChains.get(m);p.indexOf(l[c])>-1&&c++}}}catch(g){h=!0,d=g}finally{try{!f&&y["return"]&&y["return"]()}finally{if(h)throw d}}if(c===l.length)return!0}}catch(g){o=!0,a=g}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}return!1}},{key:"toJSON",value:function(){var t=un(this);return t._schema="[model.Schema]",t}}]),t}(),Id=function(){function t(){Qa(this,t),this.objects=new Set,this._items=new Map,this._extensionChains=new Map,this.registerItem("$root"),this.registerItem("$block"),this.registerItem("$inline"),this.registerItem("$text","$inline"),this.allow({name:"$block",inside:"$root"}),this.allow({name:"$inline",inside:"$block"}),this.registerItem("$clipboardHolder","$root"),this.allow({name:"$inline",inside:"$clipboardHolder"})}return Xa(t,[{key:"allow",value:function(e){this._getItem(e.name).allow(t._normalizeQueryPath(e.inside),e.attributes)}},{key:"disallow",value:function(e){this._getItem(e.name).disallow(t._normalizeQueryPath(e.inside),e.attributes)}},{key:"requireAttributes",value:function(t,e){this._getItem(t).requireAttributes(e)}},{key:"check",value:function(e){var n=this;if(!this.hasItem(e.name))return!1;ou(e.attributes)?0===e.attributes.length&&e.attributes.push(void 0):e.attributes=[e.attributes];var r=t._normalizeQueryPath(e.inside),i=this._extensionChains.get(e.name).map(function(t){return n._getItem(t)});if(!this._getItem(e.name)._checkRequiredAttributes(e.attributes))return!1;var o=!0,a=!1,s=void 0;try{for(var u,l=e.attributes[Symbol.iterator]();!(o=(u=l.next()).done);o=!0){var c=u.value,f=!0,h=!1,d=void 0;try{for(var v,y=i[Symbol.iterator]();!(f=(v=y.next()).done);f=!0){var m=v.value;if(m._hasMatchingPath("disallow",r,c))return!1}}catch(p){h=!0,d=p}finally{try{!f&&y["return"]&&y["return"]()}finally{if(h)throw d}}}}catch(p){a=!0,s=p}finally{try{!o&&l["return"]&&l["return"]()}finally{if(a)throw s}}var g=!0,b=!1,w=void 0;try{for(var k,_=e.attributes[Symbol.iterator]();!(g=(k=_.next()).done);g=!0){var x=k.value,O=!1,P=!0,S=!1,A=void 0;try{for(var C,T=i[Symbol.iterator]();!(P=(C=T.next()).done);P=!0){var E=C.value;if(E._hasMatchingPath("allow",r,x)){O=!0;break}}}catch(p){S=!0,A=p}finally{try{!P&&T["return"]&&T["return"]()}finally{if(S)throw A}}if(!O)return!1}}catch(p){b=!0,w=p}finally{try{!g&&_["return"]&&_["return"]()}finally{if(b)throw w}}return!0}},{key:"hasItem",value:function(t){return this._items.has(t)}},{key:"registerItem",value:function(t,e){if(this.hasItem(t))throw new ms("model-schema-item-exists: Item with specified name already exists in schema.");if(e&&!this.hasItem(e))throw new ms("model-schema-no-item: Item with specified name does not exist in schema.");this._items.set(t,new Md(this));var n=this.hasItem(e)?this._extensionChains.get(e).concat(t):[t];this._extensionChains.set(t,n)}},{key:"itemExtends",value:function(t,e){if(!this.hasItem(t)||!this.hasItem(e))throw new ms("model-schema-no-item: Item with specified name does not exist in schema.");var n=this._extensionChains.get(t);return n.some(function(t){return t==e})}},{key:"_getItem",value:function(t){if(!this.hasItem(t))throw new ms("model-schema-no-item: Item with specified name does not exist in schema.");return this._items.get(t)}}],[{key:"_normalizeQueryPath",value:function(t){var e=[];if(ou(t)){var n=!0,r=!1,i=void 0;try{for(var o,a=t[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;s instanceof Su?e.push(s.name):F(s)&&e.push(s)}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}}else if(t instanceof Cu){for(var l=t.parent;null!==l;)e.push(l.name),l=l.parent;e.reverse()}else F(t)&&(e=t.split(" "));return e}}]),t}(),Bd=function(){function t(){Qa(this,t),this.on("deleteContents",function(t,e){return Li(e.batch,e.selection,e.options)}),this.on("modifySelection",function(t,e){return Ji(e.selection,e.options)})}return Xa(t,[{key:"deleteContents",value:function(t,e,n){this.fire("deleteContents",{batch:t,selection:e,options:n})}},{key:"modifySelection",value:function(t,e){this.fire("modifySelection",{selection:t,options:e})}}]),t}();K(Bd,ss);var Dd="$graveyard",Ld=function(){function t(){var e=this;Qa(this,t),this.version=0,this.selection=new Nd(this),this.schema=new Id,this.history=new Ed(this),this.composer=new Bd,this._pendingChanges=[],this._roots=new Map,this.selection.on("change:range",function(){var t=!0,n=!1,r=void 0;try{for(var i,o=e.selection.getRanges()[Symbol.iterator]();!(t=(i=o.next()).done);t=!0){var a=i.value;if(!e._validateSelectionRange(a))throw new ms("document-selection-wrong-position: Range from document selection starts or ends at incorrect position.",{range:a})}}catch(s){n=!0,r=s}finally{try{!t&&o["return"]&&o["return"]()}finally{if(n)throw r}}}),this.createRoot("$root",Dd)}return Xa(t,[{key:"applyOperation",value:function(t){if(t.baseVersion!==this.version)throw new ms("model-document-applyOperation-wrong-version: Only operations with matching versions can be applied.",{operation:t});var e=t._execute();this.version++,this.history.addDelta(t.delta);var n=t.delta&&t.delta.batch;e&&this.fire("change",t.type,e,n)}},{key:"batch",value:function(t){return new lf(this,t)}},{key:"createRoot",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"$root":arguments[0],e=arguments.length<=1||void 0===arguments[1]?"main":arguments[1];if(this._roots.has(e))throw new ms("model-document-createRoot-name-exists: Root with specified name already exists.",{name:e});var n=new Xu(this,t,e);return this._roots.set(e,n),n}},{key:"destroy",value:function(){this.selection.destroy(),this.stopListening()}},{key:"enqueueChanges",value:function(t){if(this._pendingChanges.push(t),1==this._pendingChanges.length){for(;this._pendingChanges.length;)this._pendingChanges[0](),this._pendingChanges.shift();this.fire("changesDone")}}},{key:"getRoot",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"main":arguments[0];if(!this._roots.has(t))throw new ms("model-document-getRoot-root-not-exist: Root with specified name does not exist.",{name:t});return this._roots.get(t)}},{key:"hasRoot",value:function(t){return this._roots.has(t)}},{key:"getRootNames",value:function(){return Array.from(this._roots.keys()).filter(function(t){return t!=Dd})}},{key:"toJSON",value:function(){var t=un(this);return t.selection="[engine.model.LiveSelection]",t}},{key:"_getDefaultRoot",value:function(){var t=!0,e=!1,n=void 0;try{for(var r,i=this._roots.values()[Symbol.iterator]();!(t=(r=i.next()).done);t=!0){var o=r.value;if(o!==this.graveyard)return o}}catch(a){e=!0,n=a}finally{try{!t&&i["return"]&&i["return"]()}finally{if(e)throw n}}return this.graveyard}},{key:"_getDefaultRange",value:function(){var t=this._getDefaultRoot(),e=!0,n=!1,r=void 0;try{for(var i,o=Ru.createIn(t).getPositions()[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;if(this.schema.check({name:"$text",inside:a}))return new Ru(a,a)}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}var u=new Cu(t,[0]);return new Ru(u,u)}},{key:"_validateSelectionRange",value:function(t){return Yi(t.start)&&Yi(t.end)}},{key:"graveyard",get:function(){return this.getRoot(Dd)}}]),t}();K(Ld,ss);var zd=vu({},ss,{listenTo:function(){var t=Array.prototype.slice.call(arguments),e=t[0];Xi(e)&&(t[0]=this._getProxyEmitter(e)||new qd(e)),ss.listenTo.apply(this,t)},stopListening:function(){var t=Array.prototype.slice.call(arguments),e=t[0];if(Xi(e)){var n=this._getProxyEmitter(e);if(!n)return;t[0]=n}ss.stopListening.apply(this,t)},_getProxyEmitter:function(t){var e=void 0,n=void 0,r=void 0,i=Qi(t);return(n=this._listeningTo)&&(r=n[i])&&(e=r.emitter),e||null}}),qd=function Tm(t){Qa(this,Tm),this._emitterId=Qi(t),this._domNode=t};vu(qd.prototype,ss,{on:function(t,e){var n=arguments.length<=2||void 0===arguments[2]?{}:arguments[2];if(ss.on.apply(this,arguments),!this._domListeners||!this._domListeners[t]){var r=this._createDomListener(t);this._domNode.addEventListener(t,r,!!n.useCapture),this._domListeners||(this._domListeners={}),this._domListeners[t]=r}},off:function(t){ss.off.apply(this,arguments);var e=void 0;!this._domListeners[t]||(e=this._events[t])&&e.callbacks.length||this._domListeners[t].removeListener()},_createDomListener:function(t){var e=this,n=function(n){e.fire(t,n)};return n.removeListener=function(){e._domNode.removeEventListener(t,n),delete e._domListeners[t]},n}});var Hd=function(){function t(){Qa(this,t),this.set("isFocused",!1),this._elements=new Set,this._nextEventLoopTimeout=null,this._focusedElement=null}return Xa(t,[{key:"add",value:function(t){var e=this;if(this._elements.has(t))throw new ms("focusTracker-add-element-already-exist");this.listenTo(t,"focus",function(){return e._focus(t)},{useCapture:!0}),this.listenTo(t,"blur",function(){return e._blur()},{useCapture:!0}),this._elements.add(t)}},{key:"remove",value:function(t){t===this._focusedElement&&this._blur(t),this._elements.has(t)&&(this.stopListening(t),this._elements["delete"](t))}},{key:"_focus",value:function(t){clearTimeout(this._nextEventLoopTimeout),this._focusedElement=t,this.isFocused=!0}},{key:"_blur",value:function(){var t=this;this._nextEventLoopTimeout=setTimeout(function(){t._focusedElement=null,t.isFocused=!1},0)}}]),t}();K(Hd,zd),K(Hd,gu);var $d=function(){function t(e){Qa(this,t),this.config=new ys(e),this.plugins=new ku(this),this.commands=new Map,this.locale=new _u(this.config.get("lang")),this.t=this.locale.t,this.document=new Ld,this.data=new el(this.document),this.focusTracker=new Hd}return Xa(t,[{key:"initPlugins",value:function e(){function t(){var t=r.get("features")||[];return n.plugins.load(t)}function e(t){return t.reduce(function(t,e){return t.then(e.init.bind(e))},Promise.resolve())}var n=this,r=this.config;return t().then(e)}},{key:"destroy",value:function(){var t=this;return this.fire("destroy"),this.stopListening(),Promise.resolve().then(function(){t.document.destroy(),t.data.destroy()})}},{key:"execute",value:function(t,e){var n=this.commands.get(t);if(!n)throw new ms("editor-command-not-found: Specified command has not been added to the editor.");n._execute(e)}}],[{key:"create",value:function(t){var e=this;return new Promise(function(n){var r=new e(t);n(r.initPlugins().then(function(){return r}))})}}]),t}();K($d,ss);var Kd=navigator.userAgent.toLowerCase(),Wd={mac:Zi(Kd)},Jd=ro(),Ud=function(){function t(e){var n=this;Qa(this,t),this.editor=e,this._listener=Object.create(ss),this._keystrokes=new Map,this._listener.listenTo(e.editing.view,"keydown",function(t,e){var r=n.press(e);r&&e.preventDefault()})}return Xa(t,[{key:"set",value:function(t,e){this._keystrokes.set(eo(t),e)}},{key:"press",value:function(t){var e=to(t),n=this._keystrokes.get(e);return!!n&&("string"==typeof n?this.editor.execute(n):n(t),!0)}},{key:"destroy",value:function(){this._keystrokes=new Map,this._listener.stopListening()}}]),t}(),Gd=function(){function t(){Qa(this,t),this._ranges=[],this._lastRangeBackward=!1,this._isFake=!1,this._fakeSelectionLabel=""}return Xa(t,[{key:"setFake",value:function(){var t=arguments.length<=0||void 0===arguments[0]||arguments[0],e=arguments.length<=1||void 0===arguments[1]?{}:arguments[1];this._isFake=t,this._fakeSelectionLabel=t?e.label||"":"",this.fire("change")}},{key:"addRange",value:function(t,e){if(!(t instanceof Lu))throw new ms("view-selection-invalid-range: Invalid Range.");this._pushRange(t),this._lastRangeBackward=!!e,this.fire("change")}},{key:"getRanges",value:regeneratorRuntime.mark(function e(){var t,n,r,i,o,a;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:t=!0,n=!1,r=void 0,e.prev=3,i=this._ranges[Symbol.iterator]();case 5:if(t=(o=i.next()).done){e.next=12;break}return a=o.value,e.next=9,Lu.createFromRange(a);case 9:t=!0,e.next=5;break;case 12:e.next=18;break;case 14:e.prev=14,e.t0=e["catch"](3),n=!0,r=e.t0;case 18:e.prev=18,e.prev=19,!t&&i["return"]&&i["return"]();case 21:if(e.prev=21,!n){e.next=24;break}throw r;case 24:return e.finish(21);case 25:return e.finish(18);case 26:case"end":return e.stop()}},e,this,[[3,14,18,26],[19,,21,25]])})},{key:"getFirstRange",value:function(){var t=null,e=!0,n=!1,r=void 0;try{for(var i,o=this._ranges[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;t&&!a.start.isBefore(t.start)||(t=a)}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}return t?Lu.createFromRange(t):null}},{key:"getLastRange",value:function(){var t=null,e=!0,n=!1,r=void 0;try{for(var i,o=this._ranges[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;t&&!a.end.isAfter(t.end)||(t=a)}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]();
-}finally{if(n)throw r}}return t?Lu.createFromRange(t):null}},{key:"getFirstPosition",value:function(){var t=this.getFirstRange();return t?Bu.createFromPosition(t.start):null}},{key:"getLastPosition",value:function(){var t=this.getLastRange();return t?Bu.createFromPosition(t.end):null}},{key:"isEqual",value:function(t){var e=this.rangeCount;if(e!=t.rangeCount)return!1;if(this.isFake!=t.isFake)return!1;if(this.isFake&&this.fakeSelectionLabel!=t.fakeSelectionLabel)return!1;for(var n=0;n=0;a--)r.setAttribute(o[a].name,o[a].value)}if(e.withChildren||void 0===e.withChildren){var s=!0,u=!1,l=void 0;try{for(var c,f=this.domChildrenToView(t,e)[Symbol.iterator]();!(s=(c=f.next()).done);s=!0){var h=c.value;r.appendChildren(h)}}catch(d){u=!0,l=d}finally{try{!s&&f["return"]&&f["return"]()}finally{if(u)throw l}}}return r}},{key:"domChildrenToView",value:regeneratorRuntime.mark(function n(t){var e,r,i,o=arguments.length<=1||void 0===arguments[1]?{}:arguments[1];return regeneratorRuntime.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:e=0;case 1:if(!(e0){var s=!1;i?" "!=i.data.charAt(0)&&"Â "!=i.data.charAt(0)||o.length%2===0&&(s=!0):o.length%2&&(s=!0),s&&(o="Â "+o.substr(0,o.length-1)),o=o.replace(/ /g," Â ")}return a+o}},{key:"_getTouchingViewTextNode",value:function(t,e){if(!t.parent)return null;var n=new Du({startPosition:e?Bu.createAfter(t):Bu.createBefore(t),direction:e?"forward":"backward"}),r=!0,i=!1,o=void 0;try{for(var a,s=n[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;if(u.item instanceof Mu)return null;if(u.item instanceof Vu)return u.item}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return null}},{key:"_processDataFromDomText",value:function(t){var e=so(t);if(go(t,this.preElements))return e;e=e.replace(/[^\S\u00A0]{2,}/g," ");var n=this._getTouchingDomTextNode(t,!1),r=this._getTouchingDomTextNode(t,!0);return n&&!/[^\S\u00A0]/.test(n.data.charAt(n.data.length-1))||(e=e.replace(/^ /,"")),r||(e=e.replace(/ $/,"")),e=e.replace(/ \u00A0/g," "),n&&!/[^\S\u00A0]/.test(n.data.charAt(n.data.length-1))||(e=e.replace(/^\u00A0/," ")),r&&"Â "!=r.data.charAt(0)||(e=e.replace(/\u00A0( *)$/," $1")),e}},{key:"_getTouchingDomTextNode",value:function(t,e){if(!t.parentNode)return null;var n=e?"nextNode":"previousNode",r=t.ownerDocument,i=r.createTreeWalker(r.childNodes[0],NodeFilter.SHOW_TEXT);i.currentNode=t;var o=i[n]();if(null!==o){var a=po(t,o);if(a&&!go(t,this.blockElements,a)&&!go(o,this.blockElements,a))return o}return null}}]),t}(),iv=function(t){function e(t,n){var r=arguments.length<=2||void 0===arguments[2]?"main":arguments[2];Qa(this,e);var i=es(this,Object.getPrototypeOf(e).call(this,t,n));return i.rootName=r,i}return ts(e,t),e}(Iu),ov=function(){function t(e){Qa(this,t),this.document=e,this.isEnabled=!1}return Xa(t,[{key:"enable",value:function(){this.isEnabled=!0}},{key:"disable",value:function(){this.isEnabled=!1}}]),t}(),av=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n._config={childList:!0,characterData:!0,characterDataOldValue:!0,subtree:!0},n.domConverter=t.domConverter,n.renderer=t.renderer,n._domElements=[],n._mutationObserver=new window.MutationObserver(n._onMutations.bind(n)),n}return ts(e,t),Xa(e,[{key:"flush",value:function(){this._onMutations(this._mutationObserver.takeRecords())}},{key:"observe",value:function(t){this._domElements.push(t),this.isEnabled&&this._mutationObserver.observe(t,this._config)}},{key:"enable",value:function(){Za(Object.getPrototypeOf(e.prototype),"enable",this).call(this);var t=!0,n=!1,r=void 0;try{for(var i,o=this._domElements[Symbol.iterator]();!(t=(i=o.next()).done);t=!0){var a=i.value;this._mutationObserver.observe(a,this._config)}}catch(s){n=!0,r=s}finally{try{!t&&o["return"]&&o["return"]()}finally{if(n)throw r}}}},{key:"disable",value:function(){Za(Object.getPrototypeOf(e.prototype),"disable",this).call(this),this._mutationObserver.disconnect()}},{key:"_onMutations",value:function(t){if(0!==t.length){var e=this.domConverter,n=new Map,r=new Set,i=!0,o=!1,a=void 0;try{for(var s,u=t[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;if("childList"===l.type){var c=e.getCorrespondingViewElement(l.target);c&&r.add(c)}}}catch(f){o=!0,a=f}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}var h=!0,d=!1,v=void 0;try{for(var y,m=t[Symbol.iterator]();!(h=(y=m.next()).done);h=!0){var p=y.value;if("characterData"===p.type){var g=e.getCorrespondingViewText(p.target);g&&!r.has(g.parent)?n.set(g,{type:"text",oldText:g.data,newText:so(p.target),node:g}):!g&&oo(p.target)&&r.add(e.getCorrespondingViewElement(p.target.parentNode))}}}catch(f){d=!0,v=f}finally{try{!h&&m["return"]&&m["return"]()}finally{if(d)throw v}}var b=[],w=!0,k=!1,_=void 0;try{for(var x,O=n.values()[Symbol.iterator]();!(w=(x=O.next()).done);w=!0){var P=x.value;this.renderer.markToSync("text",P.node),b.push(P)}}catch(f){k=!0,_=f}finally{try{!w&&O["return"]&&O["return"]()}finally{if(k)throw _}}var S=!0,A=!1,C=void 0;try{for(var T,E=r[Symbol.iterator]();!(S=(T=E.next()).done);S=!0){var R=T.value,j=e.getCorrespondingDomElement(R),V=R.getChildren(),F=e.domChildrenToView(j);this.renderer.markToSync("children",R),b.push({type:"children",oldChildren:Array.from(V),newChildren:Array.from(F),node:R})}}catch(f){A=!0,C=f}finally{try{!S&&E["return"]&&E["return"]()}finally{if(A)throw C}}var N=t[0].target.ownerDocument.getSelection(),M=null;if(N&&N.anchorNode){var I=e.domPositionToView(N.anchorNode,N.anchorOffset),B=e.domPositionToView(N.focusNode,N.focusOffset);I&&B&&(M=new Gd,M.collapse(I),M.setFocus(B))}this.document.fire("mutations",b,M),this.document.render()}}}]),e}(ov),sv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n.mutationObserver=t.getObserver(av),n.document=t,n.selection=t.selection,n.domConverter=t.domConverter,n._documents=new WeakSet,n}return ts(e,t),Xa(e,[{key:"observe",value:function(t){var e=this,n=t.ownerDocument;this._documents.has(n)||(n.addEventListener("selectionchange",function(){return e._handleSelectionChange(n)}),this._documents.add(n))}},{key:"_handleSelectionChange",value:function(t){if(this.isEnabled&&this.document.isFocused){this.mutationObserver.flush();var e=t.defaultView.getSelection(),n=this.domConverter.domSelectionToView(e);if(!this.selection.isEqual(n)){if(this._isInfiniteLoop(n))return void wu.warn("selectionchange-infinite-loop: Selection change observer detected an infinite rendering loop.");this.document.fire("selectionChange",{oldSelection:this.selection,newSelection:n,domSelection:e}),this.document.render()}}}},{key:"_isInfiniteLoop",value:function(t){return this._lastSelection&&this._lastButOneSelection&&(t.isEqual(this._lastSelection)||t.isEqual(this._lastButOneSelection))?this._loopbackCounter++:(this._lastButOneSelection=this._lastSelection,this._lastSelection=t,this._loopbackCounter=0),this._loopbackCounter>10}}]),e}(ov),uv=function(){function t(e,n,r){Qa(this,t),this.document=e,this.domEvent=n,this.domTarget=n.target,vu(this,r)}return Xa(t,[{key:"preventDefault",value:function(){this.domEvent.preventDefault()}},{key:"stopPropagation",value:function(){this.domEvent.stopPropagation()}},{key:"target",get:function(){return this.document.domConverter.getCorrespondingViewElement(this.domTarget)}}]),t}(),lv=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"observe",value:function(t){var e=this,n="string"==typeof this.domEventType?[this.domEventType]:this.domEventType;n.forEach(function(n){t.addEventListener(n,function(t){return e.isEnabled&&e.onDomEvent(t)})})}},{key:"fire",value:function(t,e,n){this.isEnabled&&this.document.fire(t,new uv(this.document,e,n))}}]),e}(ov),cv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n.domEventType=["focus","blur"],t.on("focus",function(){t.isFocused=!0}),t.on("blur",function(e,n){var r=t.selection.editableElement;null!==r&&r!==n.target||(t.isFocused=!1)}),n}return ts(e,t),Xa(e,[{key:"onDomEvent",value:function(t){this.fire(t.type,t)}}]),e}(lv),fv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n.domEventType="keydown",n}return ts(e,t),Xa(e,[{key:"onDomEvent",value:function(t){this.fire("keydown",t,{keyCode:t.keyCode,altKey:t.altKey,ctrlKey:t.ctrlKey||t.metaKey,shiftKey:t.shiftKey,get keystroke(){return to(this)}})}}]),e}(lv),hv=function(t){function e(t){return Qa(this,e),es(this,Object.getPrototypeOf(e).call(this,t))}return ts(e,t),Xa(e,[{key:"observe",value:function(){var t=this,e=this.document;e.on("keydown",function(n,r){var i=e.selection;i.isFake&&bo(r.keyCode)&&t.isEnabled&&(r.preventDefault(),t._handleSelectionMove(r.keyCode))},{priority:"lowest"})}},{key:"_handleSelectionMove",value:function(t){var e=this.document.selection,n=Gd.createFromSelection(e);n.setFake(!1),t!=Jd.arrowleft&&t!=Jd.arrowup||n.collapseToStart(),t!=Jd.arrowright&&t!=Jd.arrowdown||n.collapseToEnd(),this.document.fire("selectionChange",{oldSelection:e,newSelection:n,domSelection:null})}}]),e}(ov),dv=function(){function t(){var e=this;Qa(this,t),this.domRoots=new Map,this.selection=new Gd,this.domConverter=new rv,this.roots=new Map,this.set("isFocused",!1),this.renderer=new nv(this.domConverter,this.selection),this.renderer.bind("isFocused").to(this,"isFocused"),this._observers=new Map,this.addObserver(av),this.addObserver(sv),this.addObserver(cv),this.addObserver(fv),this.addObserver(hv),lo(this),this.on("render",function(){e.disableObservers(),e.renderer.render(),e.enableObservers()})}return Xa(t,[{key:"addObserver",value:function(t){var e=this._observers.get(t);if(e)return e;e=new t(this),this._observers.set(t,e);var n=!0,r=!1,i=void 0;try{for(var o,a=this.domRoots[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=ns(o.value,2),u=s[0],l=s[1];e.observe(l,u)}}catch(c){r=!0,i=c}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}return e.enable(),e}},{key:"getObserver",value:function(t){return this._observers.get(t)}},{key:"createRoot",value:function(t){var e=this,n=arguments.length<=1||void 0===arguments[1]?"main":arguments[1],r="string"==typeof t?t:t.tagName,i=new iv(this,r.toLowerCase(),n);return this.roots.set(n,i),i.on("change:children",function(t,n){return e.renderer.markToSync("children",n)}),i.on("change:attributes",function(t,n){return e.renderer.markToSync("attributes",n)}),i.on("change:text",function(t,n){return e.renderer.markToSync("text",n)}),this.domConverter.isElement(t)&&this.attachDomRoot(t,n),i}},{key:"attachDomRoot",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?"main":arguments[1],n=this.getRoot(e);this.domRoots.set(e,t),this.domConverter.bindElements(t,n),this.renderer.markToSync("children",n),this.renderer.domDocuments.add(t.ownerDocument);var r=!0,i=!1,o=void 0;try{for(var a,s=this._observers.values()[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.observe(t,e)}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}}},{key:"getRoot",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"main":arguments[0];return this.roots.get(t)}},{key:"getDomRoot",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"main":arguments[0];return this.domRoots.get(t)}},{key:"render",value:function(){this.fire("render")}},{key:"focus",value:function(){if(!this.isFocused){var t=this.selection.editableElement;t?(this.domConverter.focus(t),this.render()):wu.warn("view-focus-no-selection: There is no selection in any editable to focus.")}}},{key:"disableObservers",value:function(){var t=!0,e=!1,n=void 0;try{for(var r,i=this._observers.values()[Symbol.iterator]();!(t=(r=i.next()).done);t=!0){var o=r.value;o.disable()}}catch(a){e=!0,n=a}finally{try{!t&&i["return"]&&i["return"]()}finally{if(e)throw n}}}},{key:"enableObservers",value:function(){var t=!0,e=!1,n=void 0;try{for(var r,i=this._observers.values()[Symbol.iterator]();!(t=(r=i.next()).done);t=!0){var o=r.value;o.enable()}}catch(a){e=!0,n=a}finally{try{!t&&i["return"]&&i["return"]()}finally{if(e)throw n}}}}]),t}();K(dv,gu);var vv=function(){function t(e){var n=this;Qa(this,t),this.model=e,this.view=new dv,this.mapper=new zu,this.modelToView=new Hu({mapper:this.mapper,viewSelection:this.view.selection}),this._listenter=Object.create(ss),this._listenter.listenTo(this.model,"change",function(t,e,r){n.modelToView.convertChange(e,r)},{priority:"low"}),this._listenter.listenTo(this.model,"changesDone",function(){n.modelToView.convertSelection(e.selection),n.view.render()},{priority:"low"}),this._listenter.listenTo(this.view,"selectionChange",wo(e,this.mapper)),this.modelToView.on("insert:$text",Et(),{priority:"lowest"}),this.modelToView.on("remove",Mt(),{priority:"low"}),this.modelToView.on("move",Nt(),{priority:"low"}),this.modelToView.on("rename",Bt(),{priority:"low"}),this.modelToView.on("selection",Oo(),{priority:"low"}),this.modelToView.on("selection",Po(),{priority:"low"}),this.modelToView.on("selection",ko(),{priority:"low"}),this.modelToView.on("selection",_o(),{priority:"low"})}return Xa(t,[{key:"createRoot",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?"main":arguments[1],n=this.view.createRoot(t,e),r=this.model.getRoot(e);return this.mapper.bindElements(r,n),n}},{key:"destroy",value:function(){this._listenter.stopListening()}}]),t}(),yv=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,n));return r.element=t,r.editing=new vv(r.document),r.keystrokes=new Ud(r),r}return ts(e,t),Xa(e,[{key:"destroy",value:function(){var t=this;return Promise.resolve().then(function(){return t.editing.destroy()}).then(Za(Object.getPrototypeOf(e.prototype),"destroy",this).call(this))}},{key:"setData",value:function(t){this.data.set(t)}},{key:"getData",value:function(){return this.data.get()}},{key:"updateEditorElement",value:function(){Ao(this.element,this.getData())}},{key:"loadDataFromEditorElement",value:function(){this.setData(So(this.element))}}],[{key:"create",value:function(t,e){var n=this;return new Promise(function(r){var i=new n(t,e);r(i.initPlugins().then(function(){return i}))})}}]),e}($d),mv=function(){function t(){Qa(this,t)}return Xa(t,[{key:"getHtml",value:function(t){var e=document.implementation.createHTMLDocument(""),n=e.createElement("div");return n.appendChild(t),n.innerHTML}}]),t}(),pv=function(){function t(){Qa(this,t),this._domParser=new DOMParser,this._domConverter=new rv({
-blockFiller:Qd}),this._htmlWriter=new mv}return Xa(t,[{key:"toData",value:function(t){var e=this._domConverter.viewToDom(t,document);return this._htmlWriter.getHtml(e)}},{key:"toView",value:function(t){var e=this._toDom(t);return this._domConverter.domToView(e)}},{key:"_toDom",value:function(t){for(var e=this._domParser.parseFromString(t,"text/html"),n=e.createDocumentFragment(),r=e.body.childNodes;r.length>0;)n.appendChild(r[0]);return n}}]),t}(),gv=function(){function t(e){Qa(this,t),this._items=[],this._itemMap=new Map,this._idProperty=e&&e.idProperty||"id"}return Xa(t,[{key:"add",value:function(t,e){var n=void 0,r=this._idProperty;if(r in t){if(n=t[r],"string"!=typeof n)throw new ms("collection-add-invalid-id");if(this.get(n))throw new ms("collection-add-item-already-exists")}else n=this._getNextId(),t[r]=n;if(void 0===e)e=this._items.length;else if(e>this._items.length||e<0)throw new ms("collection-add-item-invalid-index");return this._items.splice(e,0,t),this._itemMap.set(n,t),this.fire("add",t,e),this}},{key:"get",value:function(t){var e=void 0;if("string"==typeof t)e=this._itemMap.get(t);else{if("number"!=typeof t)throw new ms("collection-get-invalid-arg: Index or id must be given.");e=this._items[t]}return e||null}},{key:"remove",value:function(t){var e=void 0,n=void 0,r=void 0,i=!1,o=this._idProperty;if("string"==typeof t?(n=t,r=this._itemMap.get(n),i=!r,r&&(e=this._items.indexOf(r))):"number"==typeof t?(e=t,r=this._items[e],i=!r,r&&(n=r[o])):(r=t,n=r[o],e=this._items.indexOf(r),i=e==-1||!this._itemMap.get(n)),i)throw new ms("collection-remove-404: Item not found.");return this._items.splice(e,1),this._itemMap["delete"](n),this.fire("remove",r),r}},{key:"map",value:function(t,e){return this._items.map(t,e)}},{key:"find",value:function(t,e){return this._items.find(t,e)}},{key:"filter",value:function(t,e){return this._items.filter(t,e)}},{key:"clear",value:function(){for(;this.length;)this.remove(0)}},{key:Symbol.iterator,value:function(){return this._items[Symbol.iterator]()}},{key:"_getNextId",value:function(){var t=void 0;do t=String(os());while(this._itemMap.has(t));return t}},{key:"length",get:function(){return this._items.length}}]),t}();K(gv,ss);var bv=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this));if(!t)throw new ms("ui-controllercollection-no-name: ControllerCollection must be initialized with a name.");return r.name=t,r.locale=n,r.parent=null,r}return ts(e,t),Xa(e,[{key:"add",value:function(t,n){Za(Object.getPrototypeOf(e.prototype),"add",this).call(this,t,n);var r=Promise.resolve();return this.parent&&this.parent.ready&&!t.ready&&(r=r.then(function(){return t.init()})),r}},{key:"bind",value:function(t){function e(t){return{add:function(e,r){var a=t(e,o.locale);i.set(e,a),a&&o.add(a,"number"==typeof r?n(r):void 0)},remove:function(t){var e=i.get(t);i["delete"](e),e&&o.remove(e)}}}function n(e){for(var n=e,r=0;r'+this.sprite+"";for(var n=t.firstChild.childNodes,r=0;ralign-center align-left align-right bold bold bulletedlist image italic italic link numberedlist picker quote redo source table underline undo unlink '}),Iv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this));return n.editor=t,n.featureComponents=new _v(t),n.addCollection("body"),n}return ts(e,t),Xa(e,[{key:"init",value:function(){return this._setupIconManager(),Za(Object.getPrototypeOf(e.prototype),"init",this).call(this)}},{key:"_setupIconManager",value:function(){this.icons=Mv.icons,this.collections.get("body").add(new xv(Mv,new jv))}}]),e}(kv);K(Iv,gu);var Bv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));n.addCollection("top"),n.addCollection("main");var r=t.config;return n.set("width",r.get("ui.width")),n.set("height",r.get("ui.height")),n}return ts(e,t),e}(Iv),Dv=function(t){function e(t,n,r){Qa(this,e);var i=es(this,Object.getPrototypeOf(e).call(this,t,n));return i.editor=r,n.bind("isReadOnly","isFocused").to(t),n.set("name",t.rootName),i}return ts(e,t),e}(kv),Lv=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t)),i=r.bindTemplate;return n&&(r.element=r.editableElement=n),r.template=new Sv({tag:"div",attributes:{"class":[i.to("isFocused",function(t){return t?"ck-focused":"ck-blurred"}),"ck-editor__editable"],contenteditable:i.to("isReadOnly",function(t){return!t})}}),r}return ts(e,t),Xa(e,[{key:"init",value:function(){this.editableElement?this.template.apply(this.editableElement):this.editableElement=this.element,Za(Object.getPrototypeOf(e.prototype),"init",this).call(this)}},{key:"destroy",value:function(){Za(Object.getPrototypeOf(e.prototype),"destroy",this).call(this),this.editableElement.contentEditable=!1}}]),e}(Ev),zv=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n)),i=r.bindTemplate,o=r.t,a=function(t){return o("Rich Text Editor, %0",[t])};return Sv.extend(r.template,{attributes:{role:"textbox","aria-label":i.to("name",a),title:i.to("name",a),"class":"ck-editor__editable_inline"}}),r}return ts(e,t),e}(Lv),qv={bindToolbarItems:function(){var t=this;if(this.items=new gv,this.model.config){var e=!0,n=!1,r=void 0;try{for(var i,o=this.model.config[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;this.items.add({name:a})}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}}this.collections.get("items").bind(this.items).as(function(e){var n=e.name;return t.editor.ui.featureComponents.create(n)})}},Hv=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n));return r.addCollection("items"),r}return ts(e,t),e}(kv),$v=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n));return n.bind("isActive","limiterElement").to(t),r}return ts(e,t),e}(Hv),Kv=function(t){function e(t,n,r){Qa(this,e);var i=es(this,Object.getPrototypeOf(e).call(this,t,n));return i.editor=r,i}return ts(e,t),Xa(e,[{key:"init",value:function(){return this.bindToolbarItems(),Za(Object.getPrototypeOf(e.prototype),"init",this).call(this)}}]),e}($v);K(Kv,qv);var Wv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n.template=new Sv({tag:"div",attributes:{"class":["ck-toolbar"]}}),n.register("items",function(t){return t}),n}return ts(e,t),e}(Ev),Jv=Yo("px"),Uv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t)),r=n.bindTemplate;return n.set("isSticky",!1),n.set("limiterElement",null),n.set("limiterOffset",50),n.set("_left",null),n.set("_marginLeft",null),n.set("_isStickyToTheLimiter",!1),Sv.extend(n.template,{attributes:{"class":[r["if"]("isSticky","ck-toolbar_sticky"),r["if"]("_isStickyToTheLimiter","ck-toolbar_sticky_bottom-limit")],style:{width:r.to("isSticky",function(t){if(t){var e=window.getComputedStyle(n.element);return Jv(n._elementPlaceholder.getBoundingClientRect().width+parseFloat(e.borderLeftWidth)+parseFloat(e.borderRightWidth))}return null}),top:r.to("_isStickyToTheLimiter",function(t){return t?Jv(window.scrollY+n._limiterRect.bottom-n._toolbarRect.height-n.limiterOffset):null}),left:r.to("_left"),marginLeft:r.to("_marginLeft")}}}),n._elementPlaceholder=new Sv({tag:"div",attributes:{"class":["ck-toolbar__placeholder"],style:{display:r.to("isSticky",function(t){return t?"block":"none"}),height:r.to("isSticky",function(t){return t?Jv(n._toolbarRect.height):null})}}}).render(),n}return ts(e,t),Xa(e,[{key:"init",value:function(){var t=this;Za(Object.getPrototypeOf(e.prototype),"init",this).call(this),this.element.parentNode.insertBefore(this._elementPlaceholder,this.element),this.listenTo(window,"scroll",function(){t._checkIfShouldBeSticky()}),this.listenTo(this,"change:isActive",function(){t._checkIfShouldBeSticky()})}},{key:"destroy",value:function(){Za(Object.getPrototypeOf(e.prototype),"destroy",this).call(this),this._elementPlaceholder.remove()}},{key:"_checkIfShouldBeSticky",value:function(){var t=this._limiterRect=this.limiterElement.getBoundingClientRect(),e=this._toolbarRect=this.element.getBoundingClientRect();this.isSticky=this.isActive&&t.top<0&&this._toolbarRect.height+this.limiterOffset0?t:null}},{key:"getElementName",value:function(){return 1!=this._patterns.length||!this._patterns[0].name||this._patterns[0].name instanceof RegExp?null:this._patterns[0].name}}]),t}(),oy=function(){function t(){Qa(this,t),this._dispatchers=[],this._from=[]}return Xa(t,[{key:"for",value:function(){for(var t=arguments.length,e=Array(t),n=0;ne;)i.push(o),o=o.nextSibling;t._indentBy<0&&(i=i.reverse());var a=!0,s=!1,u=void 0;try{for(var l,c=i[Symbol.iterator]();!(a=(l=c.next()).done);a=!0){var f=l.value,h=f.getAttribute("indent")+t._indentBy;h<0?n.rename(f,"paragraph").removeAttribute(f,"indent").removeAttribute(f,"type"):n.setAttribute(f,"indent",h)}}catch(d){s=!0,u=d}finally{try{!a&&c["return"]&&c["return"]()}finally{if(s)throw u}}})}},{key:"_checkEnabled",value:function(){var t=la(this.editor.document.selection.getFirstPosition());if(!t)return!1;var e=t.previousSibling,n=t.getAttribute("indent"),r=n+this._indentBy;if(this._indentBy>0){if(!e||"listItem"!=e.name)return!1;if(e.getAttribute("indent")+1=a&&u>o&&(i=s,o=u,a=l),o===n}),this.arrow=i,this.top=t[i].top,this.left=t[i].left}}]),e}(Ev),em=function(){function t(e){Qa(this,t),Object.assign(this,Ba(e))}return Xa(t,[{key:"clone",value:function(){return new t(this)}},{key:"moveTo",value:function(t){var e=t.top,n=t.left;return this.top=e,this.right=n+this.width,this.bottom=e+this.height,this.left=n,this}},{key:"getIntersectArea",value:function(t){var e=Math.max(0,Math.min(this.right,t.right)-Math.max(this.left,t.left)),n=Math.max(0,Math.min(this.bottom,t.bottom)-Math.max(this.top,t.top));return e*n}}]),t}(),nm=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n));return n.bind("text","for").to(t),r}return ts(e,t),e}(kv),rm=function(t){function e(t,n,r,i){Qa(this,e);var o=es(this,Object.getPrototypeOf(e).call(this,t,n));return o._uid="ck-input-"+os(),o.label=o._createLabel(),o.input=o._spawnInput(r,i),o}return ts(e,t),Xa(e,[{key:"_createLabel",value:function(){var t=new Vv;return t.bind("text").to(this.model,"label"),t.set("for",this._uid),new nm(t,this.view.labelView)}},{key:"_spawnInput",value:function(t,e){return e.bind("value").to(this.model,"value"),e.set("id",this._uid),new t(e,this.view.inputView)}},{key:"value",get:function(){return this.input.value}}]),e}(kv),im=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n));return n.bind("value","id").to(t),r}return ts(e,t),Xa(e,[{key:"value",get:function(){return this.view.element.value}}]),e}(kv),om=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n)),i=r.view.t,o=new Vv({label:i("Link URL")});return o.bind("value").to(t,"url"),r.urlInput=new rm(o,n.urlInputView,im,new Vv),r.saveButton=new xy(new Vv({isEnabled:!0,isOn:!1,label:i("Save"),withText:!0,type:"submit"}),n.saveButtonView),r.cancelButton=new xy(new Vv({isEnabled:!0,isOn:!1,label:i("Cancel"),withText:!0}),n.cancelButtonView),r.unlinkButton=new xy(new Vv({isEnabled:!0,isOn:!1,label:i("Unlink"),icon:"unlink"}),n.unlinkButtonView),n.delegate("submit").to(t),r.listenTo(r.cancelButton.model,"execute",function(){r.model.fire("cancel")}),r.listenTo(r.unlinkButton.model,"execute",function(){r.model.fire("unlink")}),r.add(r.urlInput),r.add(r.saveButton),r.add(r.cancelButton),r.add(r.unlinkButton),r}return ts(e,t),e}(kv),am=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t)),r=n.bindTemplate;return n.template=new Sv({tag:"label",attributes:{"class":["ck-label"],"for":r.to("for")},children:[{text:r.to("text")}]}),n}return ts(e,t),e}(Ev),sm=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,n));return r.labelView=new am(r.locale),r.inputView=new t(r.locale),r.template=new Sv({tag:"div",children:[r.labelView,r.inputView]}),r}return ts(e,t),Xa(e,[{key:"select",value:function(){this.inputView.select()}}]),e}(Ev),um=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t)),r=n.bindTemplate;return n.template=new Sv({tag:"input",attributes:{type:"text","class":["ck-input","ck-input-text"],id:r.to("id")}}),n.on("change:value",function(t,e,r){return n.element.value=r||""}),n}return ts(e,t),Xa(e,[{key:"select",value:function(){this.element.select()}}]),e}(Ev),lm=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n.urlInputView=new sm(um,t),n.saveButtonView=new Oy(t),n.cancelButtonView=new Oy(t),n.unlinkButtonView=new Oy(t),Sv.extend(n.saveButtonView.template,{attributes:{"class":["ck-button-action"]}}),n.template=new Sv({tag:"form",attributes:{"class":["ck-link-form"]},children:[n.urlInputView,{tag:"div",attributes:{"class":["ck-link-form__actions"]},children:[n.saveButtonView,n.cancelButtonView,n.unlinkButtonView]}]}),za({view:n}),n}return ts(e,t),e}(Ev),cm=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"init",value:function(){this.editor.editing.view.addObserver(Ky),this.balloonPanel=this._createBalloonPanel(),this.form=this._createForm(),this._createToolbarLinkButton(),this._createToolbarUnlinkButton()}},{key:"_createToolbarLinkButton",value:function(){var t=this,e=this.editor,n=e.commands.get("link"),r=e.t,i=new Vv({isEnabled:!0,isOn:!1,label:r("Link"),icon:"link",keystroke:"CTRL+K"});i.bind("isEnabled").to(n,"isEnabled"),this.listenTo(i,"execute",function(){return t._showPanel()}),e.ui.featureComponents.add("link",xy,Oy,i),e.keystrokes.set("CTRL+K",function(){return t._showPanel()})}},{key:"_createToolbarUnlinkButton",value:function(){var t=this.editor,e=t.t,n=t.commands.get("unlink"),r=new Vv({isEnabled:!1,isOn:!1,label:e("Unlink"),icon:"unlink"});r.bind("isEnabled").to(n,"isEnabled"),this.listenTo(r,"execute",function(){t.execute("unlink")}),t.ui.featureComponents.add("unlink",xy,Oy,r)}},{key:"_createBalloonPanel",value:function(){var t=this,e=this.editor,n=e.editing.view,r=new Yy(new Vv({maxWidth:300}),new tm(e.locale));return e.focusTracker.add(r.view.element),this.listenTo(n,"click",function(){var e=n.selection,i=qa(e.getFirstPosition());e.isCollapsed&&i&&(t._attachPanelToElement(),t.listenTo(n,"render",function(){var n=qa(e.getFirstPosition());e.isCollapsed&&i===n?t._attachPanelToElement(i):t._hidePanel()}),t.listenTo(r.view,"change:isVisible",function(){return t.stopListening(n,"render")}))}),Ma({controller:r.view,model:r.view,activeIf:"isVisible",callback:function(){return t._hidePanel(!0)}}),Fa({controller:r.view,model:r.view,activeIf:"isVisible",contextElement:r.view.element,callback:function(){return t._hidePanel()}}),e.ui.add("body",r),r}},{key:"_createForm",value:function(){var t=this,e=this.editor,n=new Vv;n.bind("url").to(e.commands.get("link"),"value");var r=new om(n,new lm(e.locale));return this.listenTo(n,"submit",function(){e.execute("link",t.form.urlInput.value),t._hidePanel(!0)}),this.listenTo(n,"unlink",function(){e.execute("unlink"),t._hidePanel(!0)}),this.listenTo(n,"cancel",function(){return t._hidePanel(!0)}),this.balloonPanel.add("content",r),r}},{key:"_attachPanelToElement",value:function(t){var e=this.editor.editing.view,n=e.domConverter.getCorrespondingDomElement(e.selection.editableElement),r=t||qa(e.selection.getFirstPosition());r?this.balloonPanel.view.attachTo(e.domConverter.getCorrespondingDomElement(r),n):this.balloonPanel.view.attachTo(e.domConverter.viewRangeToDom(e.selection.getFirstRange()),n)}},{key:"_hidePanel",value:function(t){this.balloonPanel.view.hide(),t&&this.editor.editing.view.focus()}},{key:"_showPanel",value:function(){this._attachPanelToElement(),this.form.urlInput.view.select()}}],[{key:"requires",get:function(){return[Gy]}}]),e}(ny),fm=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"init",value:function(){var t=this,e=this.editor.t;this._addButton("numberedList",e("Numbered List")),this._addButton("bulletedList",e("Bulleted List")),this.listenTo(this.editor.editing.view,"enter",function(e,n){var r=t.editor.document,i=r.selection.getLastPosition().parent;r.selection.isCollapsed&&"listItem"==i.name&&i.isEmpty&&(t.editor.execute("outdentList"),n.preventDefault(),e.stop())}),this.listenTo(this.editor.editing.view,"keydown",function(e,n){var r=null;if(n.keystroke==eo("tab")?r="indentList":n.keystroke==eo("Shift+tab")&&(r="outdentList"),r){var i=t.editor.commands.get(r);i.isEnabled&&(t.editor.execute(r),n.preventDefault(),e.stop())}})}},{key:"_addButton",value:function(t,e){var n=this.editor,r=n.commands.get(t),i=new Vv({isEnabled:!0,isOn:!1,label:e,icon:t.toLowerCase()});i.bind("isOn","isEnabled").to(r,"value","isEnabled"),this.listenTo(i,"execute",function(){return n.execute(t)}),n.ui.featureComponents.add(t,xy,Oy,i)}}],[{key:"requires",get:function(){return[vy]}}]),e}(ny),hm=function(){function t(e){var n=this,r=arguments.length<=1||void 0===arguments[1]?20:arguments[1];Qa(this,t),this.document=e,this.size=0,this.limit=r,this._changeCallback=function(t,e,r,i){n._onBatch(i)},e.on("change",this._changeCallback)}return Xa(t,[{key:"input",value:function(t){this.size+=t,this.size>=this.limit&&this._reset()}},{key:"destroy",value:function(){this.document.off("change",this._changeCallback)}},{key:"_onBatch",value:function(t){"transparent"!=t.type&&t!==this._batch&&Ha(t.getOperations())<=1&&this._reset()}},{key:"_reset",value:function(){this._batch=null,this.size=0}},{key:"batch",get:function(){return this._batch||(this._batch=this.document.batch()),this._batch}}]),t}(),dm=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"init",value:function(){var t=this,e=this.editor,n=e.editing.view;this._buffer=new hm(e.document,e.config.get("typing.undoStep")||20),this.listenTo(n,"keydown",function(e,n){t._handleKeydown(n)},{priority:"lowest"}),this.listenTo(n,"mutations",function(e,n,r){t._handleMutations(n,r)})}},{key:"destroy",value:function(){Za(Object.getPrototypeOf(e.prototype),"destroy",this).call(this),this._buffer.destroy(),this._buffer=null}},{key:"_handleKeydown",value:function(t){var e=this,n=this.editor.document;Ka(t)||n.selection.isCollapsed||n.enqueueChanges(function(){n.composer.deleteContents(e._buffer.batch,n.selection)})}},{key:"_handleMutations",value:function(t,e){var n=this.editor.document,r=new vm(this.editor.editing,this._buffer);n.enqueueChanges(function(){return r.handle(t,e)})}}]),e}(ny),vm=function(){function t(e,n){Qa(this,t),this.editing=e,this.buffer=n,this.insertedCharacterCount=0}return Xa(t,[{key:"handle",value:function(t,e){var n=!0,r=!1,i=void 0;try{for(var o,a=t[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;this._handleTextMutation(s,e),this._handleTextNodeInsertion(s)}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}this.buffer.input(Math.max(this.insertedCharacterCount,0))}},{key:"_handleTextMutation",value:function(t,e){if("text"==t.type){for(var n=t.newText.replace(/\u00A0/g," "),r=t.oldText.replace(/\u00A0/g," "),i=fo(r,n),o=null,a=null,s=0;s0){var y=Ru.createFromPositionAndShift(v,l);this._remove(y,l)}var m=t.newText.substr(o,c);this._insert(v,m),h&&this.editing.model.selection.collapse(h)}}},{key:"_handleTextNodeInsertion",value:function(t){if("children"==t.type&&t.newChildren.length-t.oldChildren.length==1){var e=fo(t.oldChildren,t.newChildren,Wa),n=$a(e,t.newChildren);if(!(n.length>1)){var r=n[0];if(r.values[0]instanceof Vu){var i=new Bu(t.node,r.index),o=this.editing.mapper.toModelPosition(i),a=r.values[0].data;a=a.replace(/\u00A0/g," "),this._insert(o,a),this.editing.model.selection.collapse(o.parent,"end")}}}}},{key:"_insert",value:function(t,e){this.buffer.batch.weakInsert(t,e),this.insertedCharacterCount+=e.length}},{key:"_remove",value:function(t,e){this.buffer.batch.remove(t),this.insertedCharacterCount-=e}}]),t}(),ym=[to("arrowUp"),to("arrowRight"),to("arrowDown"),to("arrowLeft"),16,17,18,20,27,33,34,35,36],mm=112;mm<=135;mm++)ym.push(mm);var pm=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t));return r.direction=n,r._buffer=new hm(t.document,t.config.get("undo.step")),r}return ts(e,t),Xa(e,[{key:"_doExecute",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],n=this.editor.document;n.enqueueChanges(function(){var r=jd.createFromSelection(n.selection);if(r.isCollapsed&&n.composer.modifySelection(r,{direction:t.direction,unit:e.unit}),!r.isCollapsed){var i=0;r.getFirstRange().getMinimalFlatRanges().forEach(function(t){i+=Ha(t.getWalker({singleCharacters:!0,ignoreElementEnd:!0,shallow:!0}))}),n.composer.deleteContents(t._buffer.batch,r,{merge:!0}),t._buffer.input(i),n.selection.setRanges(r.getRanges(),r.isBackward)}})}}]),e}(sy),gm=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return t.on("keydown",function(e,n){var r={};if(n.keyCode==Jd["delete"])r.direction="forward",r.unit="character";else{if(n.keyCode!=Jd.backspace)return;r.direction="backward",r.unit="codePoint"}r.unit=n.altKey?"word":r.unit,t.fire("delete",new uv(t,n.domEvent,r))}),n}return ts(e,t),Xa(e,[{key:"observe",value:function(){}}]),e}(ov),bm=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"init",value:function(){var t=this.editor,e=t.editing.view;e.addObserver(gm),t.commands.set("forwardDelete",new pm(t,"forward")),t.commands.set("delete",new pm(t,"backward")),this.listenTo(e,"delete",function(e,n){t.execute("forward"==n.direction?"forwardDelete":"delete",{unit:n.unit}),n.preventDefault()})}}]),e}(ny),wm=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,null,[{key:"requires",get:function(){return[dm,bm]}}]),e}(ny),km=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n._stack=[],n._createdBatches=new WeakSet,n.refreshState(),n}return ts(e,t),Xa(e,[{key:"addBatch",value:function(t){var e={ranges:Array.from(this.editor.document.selection.getRanges()),isBackward:this.editor.document.selection.isBackward};this._stack.push({batch:t,selection:e}),this.refreshState()}},{key:"clearStack",value:function(){this._stack=[],this.refreshState()}},{key:"_checkEnabled",value:function(){return this._stack.length>0}},{key:"_restoreSelection",value:function(t,e,n){var r=this.editor.document,i=[],o=!0,a=!1,s=void 0;try{for(var u,l=t[Symbol.iterator]();!(o=(u=l.next()).done);o=!0){var c=u.value,f=Ua(c,n),h=f.find(function(t){return t.start.root!=r.graveyard});h&&i.push(h)}}catch(d){a=!0,s=d}finally{try{!o&&l["return"]&&l["return"]()}finally{if(a)throw s}}i.length&&r.selection.setRanges(i,e)}}]),e}(sy),_m=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"_doExecute",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]?null:arguments[0],n=e?this._stack.findIndex(function(t){return t.batch==e}):this._stack.length-1,r=this._stack.splice(n,1)[0];this.editor.document.enqueueChanges(function(){var e=t._undo(r.batch),n=t.editor.document.history.getDeltas(r.batch.baseVersion);t._restoreSelection(r.selection.ranges,r.selection.isBackward,n),t.fire("revert",r.batch,e)}),this.refreshState()}},{key:"_getItemIndexFromBaseVersion",value:function(t){for(var e=0;ei?0:i+e),n=n>i?i:n,n<0&&(n+=i),i=e>n?0:n-e>>>0,e>>>=0;for(var o=Array(i);++r0&&n(s)?e>1?Mn(s,e-1,n,r,i):De(i,s):r||(i[i.length]=s)}return i}function In(){for(var t=arguments.length,e=Array(t?t-1:0),n=arguments[0],r=t;r--;)e[r-1]=arguments[r];return t?De(ou(n)?Me(n):[n],Mn(e,1)):[]}function Bn(t,e,n){for(var r=t.length,i=e+(n?0:-1);n?i--:++i-1}function zn(t,e,n){for(var r=-1,i=t.length;++r=xh&&(o=$n,a=!1,e=new gn(e));t:for(;++i=e?t:e)),t}function _r(t){return t?kr(O(t),0,$h):0}function xr(t,e,n,r){var i=t.length;for(n=O(n),n<0&&(n=-n>i?0:i+n),r=void 0===r||r>i?i:O(r),r<0&&(r+=i),r=n>r?0:_r(r);n=120&&c.length>=120)?new gn(a&&c):void 0}c=t[0];var f=-1,h=s[0];t:for(;++f-1;)s!==t&&nd.call(s,u,1),nd.call(t,u,1);return t}function Hr(t,e){return t&&t.length&&e&&e.length?qr(t,e):t}function $r(t,e,n){return t&&t.length&&e&&e.length?qr(t,e,yr(n)):t}function Kr(t,e,n){return t&&t.length&&e&&e.length?qr(t,e,void 0,n):t}function Wr(t,e){for(var n=-1,r=null==t,i=e.length,o=Array(i);++ne||o&&a&&u&&!s&&!l||r&&a&&u||!n&&u||!i)return 1;if(!r&&!o&&!l&&t>>1,a=t[o];null!==a&&!k(a)&&(n?a<=e:a=pd){var l=e?null:md(t);if(l)return Xe(l);a=!1,i=$n,u=new gn}else u=e?[]:s;t:for(;++re._priority||!(t._priority1){var u=s[1];r.start.isTouching(u.end)?r.start=u.start:r.end.isTouching(u.start)&&(r.end=u.end)}}if(!r.start.isEqual(this.start)||!r.end.isEqual(this.end)){var l=Ru.createFromRange(this);this.start=r.start,this.end=r.end,this.fire("change",l)}}function Bi(t,e){if(t.size!=e.size)return!1;var n=!0,r=!1,i=void 0;try{for(var o,a=t.entries()[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value,u=JSON.stringify(s[1]),l=JSON.stringify(e.get(s[0]));if(u!==l)return!1}}catch(c){r=!0,i=c}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}return!0}function Di(t){return t instanceof Tu||t instanceof Pu?t.getAttributes():null}function Li(t,e){var n=arguments.length<=2||void 0===arguments[2]?{}:arguments[2];if(!e.isCollapsed){var r=e.getFirstRange(),i=r.start,o=Zu.createFromPosition(r.end);if(r.isEmpty||t.remove(r),n.merge){var a=o.path,s=Math.min(i.path.length-1,a.length-1),u=X(i.path,a);if("number"==typeof u)for(;u-1}function to(t){var e=void 0;if("string"==typeof t){if(e=Jd[t.toLowerCase()],!e)throw new ms("keyboard-unknown-key: Unknown key name.",{key:t})}else e=t.keyCode+(t.altKey?Jd.alt:0)+(t.ctrlKey?Jd.ctrl:0)+(t.shiftKey?Jd.shift:0);return e}function eo(t){return"string"==typeof t&&(t=io(t)),t.map(function(t){return"string"==typeof t?to(t):t}).reduce(function(t,e){return e+t},0)}function no(t){var e=io(t);return Wd.mac&&"ctrl"==e[0].toLowerCase()?"⌘"+(e[1]||""):t}function ro(){for(var t={arrowleft:37,arrowup:38,arrowright:39,arrowdown:40,backspace:8,"delete":46,enter:13,esc:27,tab:9,ctrl:1114112,cmd:1114112,shift:2228224,alt:4456448},e=65;e<=90;e++){var n=String.fromCharCode(e);t[n.toLowerCase()]=e}for(var r=48;r<=57;r++)t[r-48]=r;return t}function io(t){return t.split(/\s*\+\s*/)}function oo(t){return t instanceof Text&&t.data.substr(0,Xd)===Zd}function ao(t){return t.data.length==Xd&&oo(t)}function so(t){return oo(t)?t.data.slice(Xd):t.data}function uo(t,e){var n=ev.get(e);return n||(n=e(window.document),ev.set(e,n)),t.isEqualNode(n)}function lo(t){t.on("keydown",co)}function co(t,e){if(e.keyCode==Jd.arrowleft){var n=e.domTarget.ownerDocument.defaultView.getSelection();if(1==n.rangeCount&&n.getRangeAt(0).collapsed){var r=n.getRangeAt(0).startContainer,i=n.getRangeAt(0).startOffset;if(oo(r)&&i<=Xd){var o=new Range;o.setStart(r,0),o.collapse(!0),n.removeAllRanges(),n.addRange(o)}}}}function fo(t,e,n){function r(r){var a=(void 0!==f[r-1]?f[r-1]:-1)+1,l=void 0!==f[r+1]?f[r+1]:-1,h=a>l?-1:1;c[r+h]&&(c[r]=c[r+h].slice(0)),c[r]||(c[r]=[]),c[r].push(a>l?i:o);for(var d=Math.max(a,l),v=d-r;vl;d--)f[d]=r(d);f[l]=r(l),h++}while(f[l]!==u);return c[l].slice(1)}function ho(t,e,n){t.insertBefore(n,t.childNodes[e]||null)}function vo(t){var e=t.parentNode;e&&e.removeChild(t)}function yo(t){for(var e=0;t.previousSibling;)t=t.previousSibling,e++;return e}function mo(t){for(var e=[];t&&t.nodeType!=Node.DOCUMENT_NODE;)e.unshift(t),t=t.parentNode;return e}function po(t,e){for(var n=mo(t),r=mo(e),i=0;n[i]==r[i]&&n[i];)i++;return 0===i?null:n[i-1]}function go(t,e,n){var r=mo(t);return n&&(r=r.slice(r.indexOf(n)+1)),r.some(function(t){return t.tagName&&e.includes(t.tagName.toLowerCase())})}function bo(t){return t==Jd.arrowright||t==Jd.arrowleft||t==Jd.arrowup||t==Jd.arrowdown}function wo(t,e){return function(n,r){t.enqueueChanges(function(){var n=r.newSelection,i=[],o=!0,a=!1,s=void 0;try{for(var u,l=n.getRanges()[Symbol.iterator]();!(o=(u=l.next()).done);o=!0){var c=u.value;i.push(e.toModelRange(c))}}catch(f){a=!0,s=f}finally{try{!o&&l["return"]&&l["return"]()}finally{if(a)throw s}}t.selection.setRanges(i,n.isBackward)})}}function ko(){return function(t,e,n,r){var i=e.selection;if(!i.isCollapsed&&n.consume(i,"selection")){r.viewSelection.removeAllRanges();var o=!0,a=!1,s=void 0;try{for(var u,l=i.getRanges()[Symbol.iterator]();!(o=(u=l.next()).done);o=!0){var c=u.value,f=r.mapper.toViewRange(c);r.viewSelection.addRange(f,i.isBackward)}}catch(h){a=!0,s=h}finally{try{!o&&l["return"]&&l["return"]()}finally{if(a)throw s}}}}}function _o(){return function(t,e,n,r){var i=e.selection;if(i.isCollapsed&&n.consume(i,"selection")){var o=i.getFirstPosition(),a=r.mapper.toViewPosition(o),s=Ju.breakAttributes(a);r.viewSelection.removeAllRanges(),r.viewSelection.addRange(new Lu(s,s))}}}function xo(t){return function(e,n,r,i){var o=n.selection;if(o.isCollapsed&&r.consume(o,"selectionAttribute:"+n.key)){var a=i.viewSelection.getFirstPosition();i.viewSelection.removeAllRanges();var s=t instanceof Nu?t.clone(!0):t(n.value,n,o,r,i);a=Ju.wrapPosition(a,s),i.viewSelection.addRange(new Lu(a,a))}}}function Oo(){return function(t,e,n,r){var i=!0,o=!1,a=void 0;try{for(var s,u=r.viewSelection.getRanges()[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;l.isCollapsed&&l.end.parent.document&&Ju.mergeAttributes(l.start)}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}r.viewSelection.removeAllRanges()}}function Po(){return function(t,e,n,r){return r.viewSelection.setFake(!1)}}function So(t){return t instanceof HTMLTextAreaElement?t.value:t.innerHTML}function Ao(t,e){t instanceof HTMLTextAreaElement&&(t.value=e),t.innerHTML=e}function Co(t){return t.every(function(t){return"string"==typeof t})}function To(t){return t.name==wv}function Eo(t,e){return sn(t,!0,!0,e)}function Ro(t){return!!t&&(t.value&&(t=t.value),Array.isArray(t)?t.some(Ro):t instanceof Av)}function jo(t,e){return t.map(function(t){return t instanceof Av?t.getValue(e):t})}function Vo(t,e,n){var r=jo(t,e);r=1==t.length&&t[0]instanceof Tv?r[0]:r.reduce($o,""),Jo(r)?n.remove():n.set(r)}function Fo(t){return{set:function(e){t.textContent=e},remove:function(){t.textContent=""}}}function No(t,e){var n=arguments.length<=2||void 0===arguments[2]?null:arguments[2];return{set:function(r){t.setAttributeNS(n,e,r)},remove:function(){t.removeAttributeNS(n,e)}}}function Mo(t,e){return{set:function(n){t.style[e]=n},remove:function(){t.style[e]=null}}}function Io(t){var e=Eo(t,function(t){if(t&&(t instanceof Av||Uo(t)))return t});return e}function Bo(t){if("string"==typeof t?t=zo(t):t.text&&qo(t),t.on&&(t.eventListeners=Lo(t.on),delete t.on),!t.text){t.attributes&&Do(t.attributes);var e=new gv;if(t.children){var n=!0,r=!1,i=void 0;try{for(var o,a=t.children[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;e.add(Uo(s)?s:new Sv(s))}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}}t.children=e}return t}function Do(t){for(var e in t)t[e].value&&(t[e].value=[].concat(t[e].value)),Ho(t,e)}function Lo(t){for(var e in t)Ho(t,e);return t}function zo(t){return{text:[t]}}function qo(t){Array.isArray(t.text)||(t.text=[t.text])}function Ho(t,e){Array.isArray(t[e])||(t[e]=[t[e]])}function $o(t,e){return Jo(e)?t:Jo(t)?e:t+" "+e}function Ko(t,e){for(var n in e)if(t[n]){var r;(r=t[n]).push.apply(r,rs(e[n]))}else t[n]=e[n]}function Wo(t,e){if(e.attributes&&(t.attributes||(t.attributes={}),Ko(t.attributes,e.attributes)),e.eventListeners&&(t.eventListeners||(t.eventListeners={}),Ko(t.eventListeners,e.eventListeners)),e.text){var n;(n=t.text).push.apply(n,rs(e.text))}if(e.children&&e.children.length){if(t.children.length!=e.children.length)throw new ms("ui-template-extend-children-mismatch: The number of children in extended definition does not match.");var r=0,i=!0,o=!1,a=void 0;try{for(var s,u=e.children[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;Wo(t.children.get(r++),l)}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}}}function Jo(t){return!t&&0!==t}function Uo(t){return t instanceof Ev}function Go(t){return Rv.has("undefined"==typeof t?"undefined":Ya(t))&&t!==!1}function Yo(t){return function(e){return e+t}}function Qo(){return new ey}function Xo(t,e){if("function"==typeof e)return e(t);var n={};return e.name&&(n.name=Zo(e.name,t.name),!n.name)?null:e.attribute&&(n.attribute=ta(e.attribute,t),!n.attribute)?null:!(e["class"]&&(n["class"]=ea(e["class"],t),!n["class"]))&&(!(e.style&&(n.style=na(e.style,t),!n.style))&&n)}function Zo(t,e){return t instanceof RegExp?t.test(e):t===e}function ta(t,e){var n=[];for(var r in t){var i=t[r];if(!e.hasAttribute(r))return null;var o=e.getAttribute(r);if(i instanceof RegExp){if(!i.test(o))return null;n.push(r)}else{if(o!==i)return null;n.push(r)}}return n}function ea(t,e){var n=[],r=!0,i=!1,o=void 0;try{for(var a,s=t[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;if(u instanceof RegExp){var l=e.getClassNames(),c=!0,f=!1,h=void 0;try{for(var d,v=l[Symbol.iterator]();!(c=(d=v.next()).done);c=!0){var y=d.value;u.test(y)&&n.push(y)}}catch(m){f=!0,h=m}finally{try{!c&&v["return"]&&v["return"]()}finally{if(f)throw h}}if(0===n.length)return null}else{if(!e.hasClass(u))return null;n.push(u)}}}catch(m){i=!0,o=m}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return n}function na(t,e){var n=[];for(var r in t){var i=t[r];if(!e.hasStyle(r))return null;var o=e.getStyle(r);if(i instanceof RegExp){if(!i.test(o))return null;n.push(r)}else{if(o!==i)return null;n.push(r)}}return n}function ra(t,e,n,r){if(U(t)){var i=!0,o=!1,a=void 0;try{for(var s,u=t[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;ra(l,e,n,r)}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}}else{var f=Array.from(t.getAttributeKeys());f.push(e.key);var h={name:t.name||"$text",attributes:f,inside:n.context};r.schema.check(h)&&t.setAttribute(e.key,e.value)}}function ia(){return new ry}function oa(t,e){e.isEnabled=!1}function aa(t,e,n){var r=[],i=!0,o=!1,a=void 0;try{for(var s,u=e[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){for(var l=s.value,c=new Eu({boundaries:l,mergeCharacters:!0}),f=c.next(),h=l.start,d=l.start,v=l.end;!f.done;){var y=f.value.item.name||"$text";n.check({name:y,inside:h,attributes:t})||(d.isEqual(h)||r.push(new Ru(d,h)),d=c.position),h=c.position,f=c.next()}d&&!d.isEqual(v)&&r.push(new Ru(d,v))}}catch(m){o=!0,a=m}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}return r}function sa(t,e,n){if(e.isCollapsed)return n.check({name:"$text",inside:e.getFirstPosition(),attributes:t});var r=e.getRanges(),i=!0,o=!1,a=void 0;try{for(var s,u=r[Symbol.iterator]();!(i=(s=u.next()).done);i=!0)for(var l=s.value,c=new Eu({boundaries:l,mergeCharacters:!0}),f=c.position,h=c.next();!h.done;){var d=h.value.item.name||"$text";if(n.check({name:d,inside:f,attributes:t}))return!0;f=c.position,h=c.next()}}catch(v){o=!0,a=v}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}return!1}function ua(t){return t=t.replace(//g,">").replace(/\n\n/g," ").replace(/\n/g," ").replace(/^\s/," ").replace(/\s$/," ").replace(/\s\s/g," "),t.indexOf("
")>-1&&(t="
"+t+"
"),t}function la(t){return t.replace(/(\s+)<\/span>/g,function(t,e){return 1==e.length?" ":e})}function ca(t,e){var n=t.document,r=e.isCollapsed,i=e.getFirstRange(),o=i.start.parent,a=i.end.parent;if(o.root==o)return void(r||n.composer.deleteContents(t,e));if(r)fa(t,e,i.start);else{var s=i.start.isAtStart&&i.end.isAtEnd,u=o==a;n.composer.deleteContents(t,e,{merge:s}),s||(u?fa(t,e,e.focus):e.collapse(a))}}function fa(t,e,n){t.split(n),e.collapse(n.parent.nextSibling)}function ha(t){var e=arguments.length<=1||void 0===arguments[1]||arguments[1],n=t.parent;if(n instanceof Xu)return e?t.nodeAfter:t.nodeBefore;for(;!(n.parent instanceof Xu);)n=n.parent;return n}function da(t,e){return new Ru(va(t,e,!0),va(t,e,!1))}function va(t,e,n){for(var r=t.textNode||(n?t.nodeBefore:t.nodeAfter),i=null;r&&r.getAttribute("linkHref")==e;)i=r,r=n?r.previousSibling:r.nextSibling;return i?Cu.createAt(i,n?"before":"after"):t}function ya(t){var e=t.controller,n=function(e,n){return ma(n.target,t.contextElement,t.callback)};e.listenTo(t.model,"change:"+t.activeIf,function(t,r,i){i?e.listenTo(document,"mouseup",n):e.stopListening(document,"mouseup",n)}),t.model[t.activeIf]&&e.listenTo(document,"mouseup",n)}function ma(t,e,n){e.contains(t)||n()}function pa(t){var e=t.controller,n=function(e,n){return ga(n.keyCode,t.callback)};e.listenTo(t.model,"change:"+t.activeIf,function(t,r,i){i?e.listenTo(document,"keydown",n):e.stopListening(document,"keydown",n)}),t.model[t.activeIf]&&e.listenTo(document,"keydown",n)}function ga(t,e){t==Jd.esc&&e()}function ba(t){var e=document.body.getBoundingClientRect();if(t instanceof HTMLElement||t instanceof Range){var n=t.getBoundingClientRect();return{top:n.top-e.top,right:n.right-e.left,bottom:n.bottom-e.top,left:n.left-e.left,width:n.width,height:n.height}}var r=Object.assign({},t);return void 0===r.width&&(r.width=r.right-r.left),void 0===r.height&&(r.height=r.bottom-r.top),r}function wa(t){var e=ba(t),n=ka();return new Uy({top:Math.max(e.top,n.top),left:Math.max(e.left,n.left),right:Math.min(e.right,n.right),bottom:Math.min(e.bottom,n.bottom)})}function ka(){var t=window.scrollX,e=window.scrollY,n=window.innerWidth,r=window.innerHeight;return{top:e,right:n+t,bottom:r+e,left:t}}function _a(t){var e=t.view;e.listenTo(e.element,"submit",function(t,n){n.preventDefault(),e.fire("submit")},{useCapture:!0})}function xa(t){return t.parent.getAncestors().find(function(t){return t instanceof Dy})}function Oa(t){return Array.from(t.getAncestors()).find(function(t){return"listItem"==t.name})||null}function Pa(t,e){for(var n=Sa(t.getFirstPosition(),e),r=t.getLastPosition(),i=[];null!==n&&n.isBefore(r);)i.push(n.nodeAfter),n.offset++,n=Sa(n,e);return i}function Sa(t,e){var n=t.nodeAfter;for(n||(n=t.parent);null!==n&&!e.itemExtends(n.name||"$text","$block");)n=n.parent;return null!==n&&null!==n.parent?Cu.createBefore(n):null}function Aa(t,e){var n="numbered"==t.getAttribute("type")?"ol":"ul",r=new am,i=new Mu(n,null);return i.appendChildren(r),e.bindElements(t,r),r}function Ca(t,e){for(var n=e.getNext?"nextSibling":"previousSibling",r=!!e.checkAllSiblings,i=!!e.sameIndent,o=!!e.biggerIndent,a=t.getAttribute("indent"),s=t[n];s&&"listItem"==s.name;){var u=s.getAttribute("indent");if(i&&a==u||o&&au)return null;s=s[n]}return null}function Ta(t,e){t&&e&&("ul"==t.name||"ol"==t.name)&&t.name==e.name&&Ju.mergeContainers(Bu.createAfter(t))}function Ea(t,e,n){var r=e.parent,i=Ca(t,{sameIndent:!0,biggerIndent:!0});if(i){var o=n.toViewElement(i),a=Bu.createAfter(o);Ju.breakContainer(a)}var s=Ca(t,{sameIndent:!0,checkAllSiblings:!0}),u=void 0;if(s){var l=n.toViewElement(s),c=Bu.createAfter(l);u=Ju.breakContainer(c)}else{var f=t.previousSibling;u=f&&"listItem"==f.name?Bu.createAt(n.toViewElement(f),"end"):n.toViewPosition(Cu.createBefore(t))}Ju.insert(u,r);var h=Ca(t,{getNext:!0,biggerIndent:!0}),d=n.toViewElement(h);if(d){var v=Lu.createOn(d.parent),y=Bu.createAt(e,"end");Ju.move(v,y)}Ta(r,r.nextSibling),Ta(r.previousSibling,r)}function Ra(t,e,n,r){if(n.test(e.item,"insert")&&n.test(e.item,"addAttribute:type")&&n.test(e.item,"addAttribute:indent")){n.consume(e.item,"insert"),n.consume(e.item,"addAttribute:type"),n.consume(e.item,"addAttribute:indent");var i=e.item,o=Aa(i,r.mapper);Ea(i,o,r.mapper)}}function ja(t,e,n,r){if(n.consume(e.item,"changeAttribute:type")){var i=r.mapper.toViewElement(e.item);
+Ju.breakContainer(Bu.createBefore(i)),Ju.breakContainer(Bu.createAfter(i));var o=i.parent,a="numbered"==e.attributeNewValue?"ol":"ul";o=Ju.rename(o,a),Ta(o,o.nextSibling),Ta(o.previousSibling,o)}}function Va(t,e,n,r){if(n.consume(e.item,"remove")){var i=r.mapper.toViewElement(e.item);Ju.breakContainer(Bu.createBefore(i)),Ju.breakContainer(Bu.createAfter(i));var o=i.parent;Ju.remove(Lu.createOn(o))}}function Fa(t,e,n,r){if(n.consume(e.item,"move")){var i=r.mapper.toViewElement(e.item);Ju.breakContainer(Bu.createBefore(i)),Ju.breakContainer(Bu.createAfter(i));var o=i.parent,a=o.previousSibling,s=o.nextSibling,u=r.mapper.toViewPosition(e.targetPosition);"ol"!=u.parent.name&&"ul"!=u.parent.name||(u=Ju.breakContainer(u)),Ju.move(Lu.createOn(o),u),Ta(a,s),Ta(o,o.nextSibling),Ta(o.previousSibling,o)}}function Na(t,e,n,r){if(n.consume(e.item,"changeAttribute:indent")){var i=r.mapper.toViewElement(e.item);Ju.breakContainer(Bu.createBefore(i)),Ju.breakContainer(Bu.createAfter(i));var o=i.parent,a=o.previousSibling;Ju.remove(Lu.createOn(o)),Ta(a,a.nextSibling),Ea(e.item,i,r.mapper)}}function Ma(t,e,n,r){if("listItem"!=e.item.name)for(var i=r.mapper.toViewPosition(e.range.start);("ul"==i.parent.name||"ol"==i.parent.name)&&(i=Ju.breakContainer(i),null!==i.parent.parent);)i=Bu.createBefore(i.parent)}function Ia(t,e,n,r){var i=r.mapper.toViewPosition(e.sourcePosition),o=i.nodeBefore,a=i.nodeAfter;Ta(o,a)}function Ba(t,e,n,r){if(n.consume(e.input,{name:!0})){var i=new Su("listItem");e.indent=e.indent?e.indent:0;var o="ul"==e.input.parent.name?"bulleted":"numbered";i.setAttribute("type",o),i.setAttribute("indent",e.indent),e.context.push(i),e.indent++;var a=[i],s=!0,u=!1,l=void 0;try{for(var c,f=e.input.getChildren()[Symbol.iterator]();!(s=(c=f.next()).done);s=!0){var h=c.value,d=r.convertItem(h,n,e);"ul"==h.name||"ol"==h.name?a=a.concat(Array.from(d.getChildren())):i.appendChildren(d)}}catch(v){u=!0,l=v}finally{try{!s&&f["return"]&&f["return"]()}finally{if(u)throw l}}e.indent--,e.context.pop(),e.output=e.output?e.output.concat(a):a}}function Da(t,e,n){if(n.test(e.input,{name:!0})){var r=Array.from(e.input.getChildren()),i=!0,o=!1,a=void 0;try{for(var s,u=r[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;l.name&&"li"==l.name||l.remove()}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}}}function La(t,e){var n=e.modelPosition,r=e.mapper,i=n.nodeAfter;if(i&&"listItem"==i.name){var o=r.toViewElement(i);o&&0!==o.index&&(e.viewPosition=Bu.createBefore(o),t.stop())}}function za(t,e){var n=e.viewPosition,r=e.mapper,i=n.nodeAfter,o=n.nodeBefore,a=void 0;if(i)"ul"==i.name||"ol"==i.name?a=r.toModelElement(i.getChild(0)):"li"==i.name&&(a=r.toModelElement(i)),a&&(e.modelPosition=Cu.createBefore(a));else if(o){var s=void 0;if("ul"==o.name||"ol"==o.name?s=o.getChild(o.childCount-1):"li"==o.name&&(s=o),s){a=r.toModelElement(s);var u=r.getModelLength(s);e.modelPosition=Cu.createBefore(a).getShiftedBy(u)}}null!==e.modelPosition&&t.stop()}function qa(t){var e=0,n=!0,r=!1,i=void 0;try{for(var o,a=t[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){o.value;e++}}catch(s){r=!0,i=s}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}return e}function Ha(t,e){function n(){a&&(i.push(a),a=null)}function r(t){return a&&a.type==t}var i=[],o=0,a=void 0;return t.forEach(function(t){"equal"==t?(n(),o++):"insert"==t?(r("insert")?a.values.push(e[o]):(n(),a={type:"insert",index:o,values:[e[o]]}),o++):r("delete")?a.howMany++:(n(),a={type:"delete",index:o,howMany:1})}),n(),i}function $a(t){return!!t.ctrlKey||hm.includes(t.keyCode)}function Ka(t,e){return t instanceof Vu&&e instanceof Vu?t.data===e.data:t===e}function Wa(t,e){var n=!(arguments.length<=2||void 0===arguments[2])&&arguments[2],r=[],i=!0,o=!1,a=void 0;try{for(var s,u=t[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){for(var l=s.value,c=[l],f=0;f=0;--r){var i=this.tryEntries[r],o=i.completion;if("root"===i.tryLoc)return e("end");if(i.tryLoc<=this.prev){var a=m.call(i,"catchLoc"),s=m.call(i,"finallyLoc");if(a&&s){if(this.prev=0;--n){var r=this.tryEntries[n];if(r.tryLoc<=this.prev&&m.call(r,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),f(n),S}},"catch":function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var r=n.completion;if("throw"===r.type){var i=r.arg;f(n)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,n){return this.delegate={iterator:d(t),resultName:e,nextLoc:n},S}}}("object"===("undefined"==typeof global?"undefined":Ya(global))?global:"object"===("undefined"==typeof window?"undefined":Ya(window))?window:"object"===("undefined"==typeof self?"undefined":Ya(self))?self:void 0);var is=function Cm(e,n){Qa(this,Cm),this.source=e,this.name=n,this.path=[],this.stop=t(),this.off=t()},os=function(){var t=1;return function(){return t++}}(),as={get:function(t){return"number"!=typeof t?this[t]||this.normal:t},highest:1e5,high:1e3,normal:0,low:-1e3,lowest:-1e5},ss={on:function(t,e){var n=arguments.length<=2||void 0===arguments[2]?{}:arguments[2];r(this,t);var o=i(this,t),a=as.get(n.priority);e={callback:e,context:n.context||this,priority:a};var s=!0,u=!1,l=void 0;try{for(var c,f=o[Symbol.iterator]();!(s=(c=f.next()).done);s=!0){for(var h=c.value,d=!1,v=0;v1?i-1:0),s=1;s=e&&t0){t.children=[];var n=!0,r=!1,i=void 0;try{for(var o,a=this._children[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;t.children.push(s.toJSON())}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}}return t}},{key:"childCount",get:function(){return this._children.length}},{key:"maxOffset",get:function(){return this._children.maxOffset}},{key:"isEmpty",get:function(){return 0===this.childCount}}],[{key:"fromJSON",value:function(t){var n=null;if(t.children){n=[];var r=!0,i=!1,o=void 0;try{for(var a,s=t.children[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.name?n.push(e.fromJSON(u)):n.push(Pu.fromJSON(u))}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}}return new e(t.name,t.attributes,n)}}]),e}(xu),Au=function(){function t(e){Qa(this,t),this._children=new Ou,e&&this.insertChildren(0,e)}return Xa(t,[{key:Symbol.iterator,value:function(){return this.getChildren()}},{key:"getChild",value:function(t){return this._children.getNode(t)}},{key:"getChildren",value:function(){return this._children[Symbol.iterator]()}},{key:"getChildIndex",value:function(t){return this._children.getNodeIndex(t)}},{key:"getChildStartOffset",value:function(t){return this._children.getNodeStartOffset(t)}},{key:"getPath",value:function(){return[]}},{key:"offsetToIndex",value:function(t){return this._children.offsetToIndex(t)}},{key:"appendChildren",value:function(t){this.insertChildren(this.childCount,t)}},{key:"insertChildren",value:function(t,e){e=Y(e);var n=!0,r=!1,i=void 0;try{for(var o,a=e[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;s.parent=this}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}this._children.insertNodes(t,e)}},{key:"removeChildren",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?1:arguments[1],n=this._children.removeNodes(t,e),r=!0,i=!1,o=void 0;try{for(var a,s=n[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.parent=null}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return n}},{key:"toJSON",value:function(){var t=[],e=!0,n=!1,r=void 0;try{for(var i,o=this._children[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;t.push(a.toJSON())}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}return t}},{key:"childCount",get:function(){return this._children.length}},{key:"maxOffset",get:function(){return this._children.maxOffset}},{key:"isEmpty",get:function(){return 0===this.childCount}},{key:"root",get:function(){return this}},{key:"parent",get:function(){return null}}],[{key:"fromJSON",value:function(e){var n=[],r=!0,i=!1,o=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.name?n.push(Su.fromJSON(u)):n.push(Pu.fromJSON(u))}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return new t(n)}}]),t}(),Cu=function(){function t(e,n){if(Qa(this,t),!(e instanceof Su||e instanceof Au))throw new ms("model-position-root-invalid: Position root invalid.");if(!(n instanceof Array)||0===n.length)throw new ms("model-position-path-incorrect: Position path must be an Array with at least one item.",{path:n});n=e.getPath().concat(n),e=e.root,this.root=e,this.path=n}return Xa(t,[{key:"compareWith",value:function(t){if(this.root!=t.root)return"different";var e=X(this.path,t.path);
+switch(e){case"same":return"same";case"prefix":return"before";case"extension":return"after";default:return this.path[e]r.path.length){if(n.offset!==o.maxOffset)return!1;n.path=n.path.slice(0,-1),o=o.parent,n.offset++}else{if(0!==r.offset)return!1;r.path=r.path.slice(0,-1)}}}},{key:"_getTransformedByDeletion",value:function(e,n){var r=t.createFromPosition(this);if(this.root!=e.root)return r;if("same"==X(e.getParentPath(),this.getParentPath())){if(e.offsetthis.offset)return null;r.offset-=n}}else if("prefix"==X(e.getParentPath(),this.getParentPath())){var i=e.path.length-1;if(e.offset<=this.path[i]){if(e.offset+n>this.path[i])return null;r.path[i]-=n}}return r}},{key:"_getTransformedByInsertion",value:function(e,n,r){var i=t.createFromPosition(this);if(this.root!=e.root)return i;if("same"==X(e.getParentPath(),this.getParentPath()))(e.offsete.offsetSize)throw new ms("model-textproxy-wrong-offsetintext: Given offsetInText value is incorrect.");if(r<0||n+r>e.offsetSize)throw new ms("model-textproxy-wrong-length: Given length value is incorrect.");this.data=e.data.substring(n,n+r),this.offsetInText=n}return Xa(t,[{key:"getPath",value:function(){var t=this.textNode.getPath();return t.length>0&&(t[t.length-1]+=this.offsetInText),t}},{key:"getAncestors",value:function(){for(var t=arguments.length<=0||void 0===arguments[0]?{includeNode:!1,parentFirst:!1}:arguments[0],e=[],n=t.includeNode?this:this.parent;n;)e[t.parentFirst?"push":"unshift"](n),n=n.parent;return e}},{key:"hasAttribute",value:function(t){return this.textNode.hasAttribute(t)}},{key:"getAttribute",value:function(t){return this.textNode.getAttribute(t)}},{key:"getAttributes",value:function(){return this.textNode.getAttributes()}},{key:"getAttributeKeys",value:function(){return this.textNode.getAttributeKeys()}},{key:"startOffset",get:function(){return null!==this.textNode.startOffset?this.textNode.startOffset+this.offsetInText:null}},{key:"offsetSize",get:function(){return this.data.length}},{key:"endOffset",get:function(){return null!==this.startOffset?this.startOffset+this.offsetSize:null}},{key:"isPartial",get:function(){return this.offsetSize!==this.textNode.offsetSize}},{key:"parent",get:function(){return this.textNode.parent}},{key:"root",get:function(){return this.textNode.root}},{key:"document",get:function(){return this.textNode.document}}]),t}(),Eu=function(){function t(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];if(Qa(this,t),!e.boundaries&&!e.startPosition)throw new ms("model-tree-walker-no-start-position: Neither boundaries nor starting position have been defined.");var n=e.direction||"forward";if("forward"!=n&&"backward"!=n)throw new ms("model-tree-walker-unknown-direction: Only `backward` and `forward` direction allowed.",{direction:n});this.direction=n,this.boundaries=e.boundaries||null,e.startPosition?this.position=Cu.createFromPosition(e.startPosition):this.position=Cu.createFromPosition(this.boundaries["backward"==this.direction?"end":"start"]),this.singleCharacters=!!e.singleCharacters,this.shallow=!!e.shallow,this.ignoreElementEnd=!!e.ignoreElementEnd,this._boundaryStartParent=this.boundaries?this.boundaries.start.parent:null,this._boundaryEndParent=this.boundaries?this.boundaries.end.parent:null,this._visitedParent=this.position.parent}return Xa(t,[{key:Symbol.iterator,value:function(){return this}},{key:"next",value:function(){return"forward"==this.direction?this._next():this._previous()}},{key:"_next",value:function(){var t=this.position,e=Cu.createFromPosition(this.position),n=this._visitedParent;if(null===n.parent&&e.offset===n.maxOffset)return{done:!0};if(n===this._boundaryEndParent&&e.offset==this.boundaries.end.offset)return{done:!0};var r=e.textNode?e.textNode:e.nodeAfter;if(r instanceof Su)return this.shallow?e.offset++:(e.path.push(0),this._visitedParent=r),this.position=e,Z("elementStart",r,t,e,1);if(r instanceof Pu){var i=void 0,o=void 0;if(this.singleCharacters)i=1;else{var a=r.endOffset;this._boundaryEndParent==n&&this.boundaries.end.offseta&&(a=this.boundaries.start.offset),i=e.offset-a}o=e.offset-r.startOffset;var s=new Tu(r,o-i,i);return e.offset-=i,this.position=e,Z("text",s,t,e,i)}return e.path.pop(),this.position=e,this._visitedParent=n.parent,Z("elementStart",n,t,e,1)}}]),t}(),Ru=function(){function t(e){var n=arguments.length<=1||void 0===arguments[1]?null:arguments[1];Qa(this,t),this.start=Cu.createFromPosition(e),this.end=n?Cu.createFromPosition(n):Cu.createFromPosition(e)}return Xa(t,[{key:Symbol.iterator,value:regeneratorRuntime.mark(function e(){return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.delegateYield(new Eu({boundaries:this,ignoreElementEnd:!0}),"t0",1);case 1:case"end":return t.stop()}},e,this)})},{key:"containsPosition",value:function(t){return t.isAfter(this.start)&&t.isBefore(this.end)}},{key:"containsRange",value:function(t){return this.containsPosition(t.start)&&this.containsPosition(t.end)}},{key:"isEqual",value:function(t){return this.start.isEqual(t.start)&&this.end.isEqual(t.end)}},{key:"isIntersecting",value:function(t){return this.start.isBefore(t.end)&&this.end.isAfter(t.start)}},{key:"getDifference",value:function(e){var n=[];return this.isIntersecting(e)?(this.containsPosition(e.start)&&n.push(new t(this.start,e.start)),this.containsPosition(e.end)&&n.push(new t(e.end,this.end))):n.push(t.createFromRange(this)),n}},{key:"getIntersection",value:function(e){if(this.isIntersecting(e)){var n=this.start,r=this.end;return this.containsPosition(e.start)&&(n=e.start),this.containsPosition(e.end)&&(r=e.end),new t(n,r)}return null}},{key:"getMinimalFlatRanges",value:function(){for(var e=[],n=X(this.start.path,this.end.path),r="string"==typeof n?Math.min(this.start.path.length,this.end.path.length):n,i=Cu.createFromPosition(this.start),o=i.parent;i.path.length>r+1;){var a=o.maxOffset-i.offset;0!==a&&e.push(new t(i,i.getShiftedBy(a))),i.path=i.path.slice(0,-1),i.offset++,o=o.parent}for(;i.path.length<=this.end.path.length;){var s=this.end.path[i.path.length-1],u=s-i.offset;0!==u&&e.push(new t(i,i.getShiftedBy(u))),i.offset=s,i.path.push(0)}return e}},{key:"getWalker",value:function(){var t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return t.boundaries=this,new Eu(t)}},{key:"getItems",value:regeneratorRuntime.mark(function n(){var t,e,r,i,o,a,s,u=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return regeneratorRuntime.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:u.boundaries=this,u.ignoreElementEnd=!0,t=new Eu(u),e=!0,r=!1,i=void 0,n.prev=6,o=t[Symbol.iterator]();case 8:if(e=(a=o.next()).done){n.next=15;break}return s=a.value,n.next=12,s.item;case 12:e=!0,n.next=8;break;case 15:n.next=21;break;case 17:n.prev=17,n.t0=n["catch"](6),r=!0,i=n.t0;case 21:n.prev=21,n.prev=22,!e&&o["return"]&&o["return"]();case 24:if(n.prev=24,!r){n.next=27;break}throw i;case 27:return n.finish(24);case 28:return n.finish(21);case 29:case"end":return n.stop()}},n,this,[[6,17,21,29],[22,,24,28]])})},{key:"getPositions",value:regeneratorRuntime.mark(function r(){var t,e,n,i,o,a,s,u=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:return u.boundaries=this,t=new Eu(u),r.next=4,t.position;case 4:e=!0,n=!1,i=void 0,r.prev=7,o=t[Symbol.iterator]();case 9:if(e=(a=o.next()).done){r.next=16;break}return s=a.value,r.next=13,s.nextPosition;case 13:e=!0,r.next=9;break;case 16:r.next=22;break;case 18:r.prev=18,r.t0=r["catch"](7),n=!0,i=r.t0;case 22:r.prev=22,r.prev=23,!e&&o["return"]&&o["return"]();case 25:if(r.prev=25,!n){r.next=28;break}throw i;case 28:return r.finish(25);case 29:return r.finish(22);case 30:case"end":return r.stop()}},r,this,[[7,18,22,30],[23,,25,29]])})},{key:"_getTransformedByInsertion",value:function(e,n){var r=!(arguments.length<=2||void 0===arguments[2])&&arguments[2],i=!(arguments.length<=3||void 0===arguments[3])&&arguments[3];if(r&&this.containsPosition(e))return[new t(this.start,e),new t(e._getTransformedByInsertion(e,n,!0),this.end._getTransformedByInsertion(e,n,this.isCollapsed))];var o=t.createFromRange(this),a=o.isCollapsed?i:!i,s=i;return o.start=o.start._getTransformedByInsertion(e,n,a),o.end=o.end._getTransformedByInsertion(e,n,s),[o]}},{key:"_getTransformedByMove",value:function(e,n,r,i){var o=!(arguments.length<=4||void 0===arguments[4])&&arguments[4],a=void 0,s=new t(e,e.getShiftedBy(r)),u=this.getDifference(s),l=void 0;l=1==u.length?new t(u[0].start._getTransformedByDeletion(e,r),u[0].end._getTransformedByDeletion(e,r)):2==u.length?new t(this.start,this.end._getTransformedByDeletion(e,r)):null;var c=n._getTransformedByDeletion(e,r);a=l?l._getTransformedByInsertion(c,r,i,o):[];var f=this.getIntersection(s);return!f||!i&&null!==l&&l.containsPosition(c)||a.push(new t(f.start._getCombined(s.start,c),f.end._getCombined(s.start,c))),a}},{key:"isCollapsed",get:function(){return this.start.isEqual(this.end)}},{key:"isFlat",get:function(){return this.start.parent===this.end.parent}},{key:"isEmpty",get:function(){return this.start.isTouching(this.end)}},{key:"root",get:function(){return this.start.root}}],[{key:"createFromPositionAndShift",value:function(t,e){var n=t,r=t.getShiftedBy(e);return e>0?new this(n,r):new this(r,n)}},{key:"createFromParentsAndOffsets",value:function(t,e,n,r){return new this(Cu.createFromParentAndOffset(t,e),Cu.createFromParentAndOffset(n,r))}},{key:"createFromRange",value:function(t){return new this(t.start,t.end)}},{key:"createIn",value:function(t){return this.createFromParentsAndOffsets(t,0,t,t.maxOffset)}},{key:"createOn",value:function(t){return this.createFromPositionAndShift(Cu.createBefore(t),t.offsetSize)}},{key:"fromJSON",value:function(t,e){return new this(Cu.fromJSON(t.start,e),Cu.fromJSON(t.end,e))}}]),t}(),ju=function(){function t(){Qa(this,t),this.parent=null}return Xa(t,[{key:"getAncestors",value:function(){for(var t=arguments.length<=0||void 0===arguments[0]?{includeNode:!1,parentFirst:!1}:arguments[0],e=[],n=t.includeNode?this:this.parent;n;)e[t.parentFirst?"push":"unshift"](n),n=n.parent;return e}},{key:"remove",value:function(){this.parent.removeChildren(this.index)}},{key:"_fireChange",value:function(t,e){this.fire("change:"+t,e),this.parent&&this.parent._fireChange(t,e)}},{key:"index",get:function(){var t=void 0;if(!this.parent)return null;if((t=this.parent.getChildIndex(this))==-1)throw new ms("view-node-not-found-in-parent: The node's parent does not contain this node.");return t}},{key:"nextSibling",get:function(){var t=this.index;return null!==t&&this.parent.getChild(t+1)||null}},{key:"previousSibling",get:function(){var t=this.index;return null!==t&&this.parent.getChild(t-1)||null}},{key:"root",get:function(){for(var t=this;t.parent;)t=t.parent;return t}},{key:"document",get:function(){return this.parent instanceof t?this.parent.document:null}}]),t}();K(ju,ss);var Vu=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this));return n._data=t,n}return ts(e,t),Xa(e,[{key:"clone",value:function(){return new e(this.data)}},{key:"isSimilar",value:function(t){return t instanceof e&&(this===t||this.data===t.data)}},{key:"data",get:function(){return this._data},set:function(t){this._fireChange("text",this),this._data=t}}]),e}(ju),Fu=function(){function t(e,n,r){if(Qa(this,t),this.textNode=e,n<0||n>e.data.length)throw new ms("view-textproxy-wrong-offsetintext: Given offsetInText value is incorrect.");if(r<0||n+r>e.data.length)throw new ms("view-textproxy-wrong-length: Given length value is incorrect.");this.data=e.data.substring(n,n+r),this.offsetInText=n}return Xa(t,[{key:"getAncestors",value:function(){for(var t=arguments.length<=0||void 0===arguments[0]?{includeNode:!1,parentFirst:!1}:arguments[0],e=[],n=t.includeNode?this.textNode:this.parent;null!==n;)e[t.parentFirst?"push":"unshift"](n),n=n.parent;return e}},{key:"isPartial",get:function(){return this.data.length!==this.textNode.data.length}},{key:"parent",get:function(){return this.textNode.parent}},{key:"root",get:function(){return this.textNode.root}},{key:"document",get:function(){return this.textNode.document}}]),t}(),Nu=function(t){function e(t,n,r){Qa(this,e);var i=es(this,Object.getPrototypeOf(e).call(this));if(i.name=t,l(n)?i._attrs=W(n):i._attrs=new Map(n),i._children=[],r&&i.insertChildren(0,r),i._classes=new Set,i._attrs.has("class")){var o=i._attrs.get("class");et(i._classes,o),i._attrs["delete"]("class")}return i._styles=new Map,i._attrs.has("style")&&(tt(i._styles,i._attrs.get("style")),i._attrs["delete"]("style")),i}return ts(e,t),Xa(e,[{key:"clone",value:function(t){var e=[];if(t){var n=!0,r=!1,i=void 0;try{for(var o,a=this.getChildren()[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;e.push(s.clone(t))}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}}var l=new this.constructor(this.name,this._attrs,e);return l._classes=new Set(this._classes),l._styles=new Map(this._styles),l}},{key:"appendChildren",value:function(t){return this.insertChildren(this.childCount,t)}},{key:"getChild",value:function(t){return this._children[t]}},{key:"getChildIndex",value:function(t){return this._children.indexOf(t)}},{key:"getChildren",value:function(){return this._children[Symbol.iterator]()}},{key:"getAttributeKeys",value:regeneratorRuntime.mark(function n(){var t,e,r,i,o,a;return regeneratorRuntime.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:if(!(this._classes.size>0)){n.next=3;break}return n.next=3,"class";case 3:if(!(this._styles.size>0)){n.next=6;break}return n.next=6,"style";case 6:t=!0,e=!1,r=void 0,n.prev=9,i=this._attrs.keys()[Symbol.iterator]();case 11:if(t=(o=i.next()).done){n.next=18;break}return a=o.value,n.next=15,a;case 15:t=!0,n.next=11;break;case 18:n.next=24;break;case 20:n.prev=20,n.t0=n["catch"](9),e=!0,r=n.t0;case 24:n.prev=24,n.prev=25,!t&&i["return"]&&i["return"]();case 27:if(n.prev=27,!e){n.next=30;break}throw r;case 30:return n.finish(27);case 31:return n.finish(24);case 32:case"end":return n.stop()}},n,this,[[9,20,24,32],[25,,27,31]])})},{key:"getAttributes",value:regeneratorRuntime.mark(function r(){return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return t.delegateYield(this._attrs.entries(),"t0",1);case 1:if(!(this._classes.size>0)){t.next=4;break}return t.next=4,["class",this.getAttribute("class")];case 4:if(!(this._styles.size>0)){t.next=7;break}return t.next=7,["style",this.getAttribute("style")];case 7:case"end":return t.stop()}},r,this)})},{key:"getAttribute",value:function(t){if("class"!=t){if("style"!=t)return this._attrs.get(t);if(this._styles.size>0){var e="",n=!0,r=!1,i=void 0;try{for(var o,a=this._styles[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=ns(o.value,2),u=s[0],l=s[1];e+=u+":"+l+";"}}catch(c){r=!0,i=c}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}return e}}else if(this._classes.size>0)return[].concat(rs(this._classes)).join(" ")}},{key:"hasAttribute",value:function(t){return"class"==t?this._classes.size>0:"style"==t?this._styles.size>0:this._attrs.has(t)}},{key:"setAttribute",value:function(t,e){this._fireChange("attributes",this),"class"==t?et(this._classes,e):"style"==t?tt(this._styles,e):this._attrs.set(t,e)}},{key:"insertChildren",value:function(t,e){this._fireChange("children",this);var n=0;e=nt(e);var r=!0,i=!1,o=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.parent=this,this._children.splice(t,0,u),t++,n++}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return n}},{key:"removeAttribute",value:function(t){return this._fireChange("attributes",this),"class"==t?this._classes.size>0&&(this._classes.clear(),!0):"style"==t?this._styles.size>0&&(this._styles.clear(),!0):this._attrs["delete"](t)}},{key:"removeChildren",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?1:arguments[1];this._fireChange("children",this);for(var n=t;n0?new this(n,r):new this(r,n)}},{key:"createIn",value:function(t){return this.createFromParentsAndOffsets(t,0,t,t.childCount)}},{key:"createOn",value:function(t){return this.createFromPositionAndShift(Bu.createBefore(t),1)}}]),t}(),zu=function(){function t(){var e=this;Qa(this,t),this._modelToViewMapping=new WeakMap,this._viewToModelMapping=new WeakMap,this._viewToModelLengthCallbacks=new Map,this.on("modelToViewPosition",function(t,n){var r=e._modelToViewMapping.get(n.modelPosition.parent);n.viewPosition=e._findPositionIn(r,n.modelPosition.offset)},{priority:"lowest"}),this.on("viewToModelPosition",function(t,n){for(var r=n.viewPosition.parent,i=e._viewToModelMapping.get(r);!i;)r=r.parent,i=e._viewToModelMapping.get(r);var o=e._toModelOffset(n.viewPosition.parent,n.viewPosition.offset,r);n.modelPosition=Cu.createFromParentAndOffset(i,o)},{priority:"lowest"})}return Xa(t,[{key:"bindElements",value:function(t,e){this._modelToViewMapping.set(t,e),this._viewToModelMapping.set(e,t)}},{key:"unbindViewElement",value:function(t){var e=this.toModelElement(t);this._unbindElements(e,t)}},{key:"unbindModelElement",value:function(t){var e=this.toViewElement(t);this._unbindElements(t,e)}},{key:"clearBindings",value:function(){this._modelToViewMapping=new WeakMap,this._viewToModelMapping=new WeakMap}},{key:"toModelElement",value:function(t){return this._viewToModelMapping.get(t)}},{key:"toViewElement",value:function(t){return this._modelToViewMapping.get(t)}},{key:"toModelRange",value:function(t){return new Ru(this.toModelPosition(t.start),this.toModelPosition(t.end))}},{key:"toViewRange",value:function(t){return new Lu(this.toViewPosition(t.start),this.toViewPosition(t.end))}},{key:"toModelPosition",value:function(t){var e={viewPosition:t,modelPosition:null,mapper:this};return this.fire("viewToModelPosition",e),e.modelPosition}},{key:"toViewPosition",value:function(t){var e={viewPosition:null,modelPosition:t,mapper:this};return this.fire("modelToViewPosition",e),e.viewPosition}},{key:"registerViewToModelLength",value:function(t,e){this._viewToModelLengthCallbacks.set(t,e)}},{key:"_toModelOffset",value:function(t,e,n){if(n!=t){var r=this._toModelOffset(t.parent,t.index,n),i=this._toModelOffset(t,e,t);return r+i}if(t instanceof Vu)return e;for(var o=0,a=0;a1)return null;t=t.parent}return!t||t.childCount>1?null:0}}]),e}(Nu);Ku.DEFAULT_PRIORITY=$u;var Wu=function(){function t(e){Qa(this,t),this._children=[],e&&this.insertChildren(0,e)}return Xa(t,[{key:Symbol.iterator,value:function(){return this._children[Symbol.iterator]()}},{key:"getAncestors",value:function(){return[]}},{key:"appendChildren",value:function(t){return this.insertChildren(this.childCount,t)}},{key:"getChild",value:function(t){return this._children[t]}},{key:"getChildIndex",value:function(t){return this._children.indexOf(t)}},{key:"getChildren",value:function(){return this._children[Symbol.iterator]()}},{key:"insertChildren",value:function(t,e){this._fireChange("children",this);var n=0;e=rt(e);var r=!0,i=!1,o=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.parent=this,this._children.splice(t,0,u),t++,n++}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return n}},{key:"removeChildren",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?1:arguments[1];this._fireChange("children",this);for(var n=t;nt.maxOffset)throw new ms("move-operation-nodes-do-not-exist: The nodes which should be moved do not exist.");if(t===e&&n=n&&this.targetPosition.path[i]0?this.operations[0].baseVersion:null},set:function(t){var e=!0,n=!1,r=void 0;try{for(var i,o=this.operations[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;a.baseVersion=t++}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}}},{key:"_reverseDeltaClass",get:function(){return t}}],[{key:"className",get:function(){return"engine.model.delta.Delta"}},{key:"_priority",get:function(){return 0}}]),t}();sf.register(uf);var lf=function(){function t(e){var n=arguments.length<=1||void 0===arguments[1]?"default":arguments[1];Qa(this,t),this.document=e,this.deltas=[],this.type=n}return Xa(t,[{key:"addDelta",value:function(t){return t.batch=this,this.deltas.push(t),t}},{key:"getOperations",value:regeneratorRuntime.mark(function e(){var t,n,r,i,o,a;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:t=!0,n=!1,r=void 0,e.prev=3,i=this.deltas[Symbol.iterator]();case 5:if(t=(o=i.next()).done){e.next=11;break}return a=o.value,e.delegateYield(a.operations,"t0",8);case 8:t=!0,e.next=5;break;case 11:e.next=17;break;case 13:e.prev=13,e.t1=e["catch"](3),n=!0,r=e.t1;case 17:e.prev=17,e.prev=18,!t&&i["return"]&&i["return"]();case 20:if(e.prev=20,!n){e.next=23;break}throw r;case 23:return e.finish(20);case 24:return e.finish(17);case 25:case"end":return e.stop()}},e,this,[[3,13,17,25],[18,,20,24]])})},{key:"baseVersion",get:function(){return this.deltas.length>0?this.deltas[0].baseVersion:null}}]),t}(),cf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"key",get:function(){return this.operations[0]?this.operations[0].key:null}},{key:"value",get:function(){return this.operations[0]?this.operations[0].newValue:null}},{key:"range",get:function(){if(this._range)return this._range;var t=this.operations[0],e=this.operations[this.operations.length-1];return t?(this._range=new Ru(t.range.start,e.range.end),this._range):null}},{key:"_reverseDeltaClass",get:function(){return e}}],[{key:"className",get:function(){return"engine.model.delta.AttributeDelta"}},{key:"_priority",get:function(){return 20}}]),e}(uf),ff=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,null,[{key:"className",get:function(){return"engine.model.delta.RootAttributeDelta"}}]),e}(uf);ln("setAttribute",function(t,e,n){return cn(this,e,n,t),this}),ln("removeAttribute",function(t,e){return cn(this,e,null,t),this}),sf.register(cf),sf.register(ff);var hf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"howMany",get:function(){return this._moveOperation?this._moveOperation.howMany:null}},{key:"sourcePosition",get:function(){return this._moveOperation?this._moveOperation.sourcePosition:null}},{key:"targetPosition",get:function(){return this._moveOperation?this._moveOperation.targetPosition:null}},{key:"_moveOperation",get:function(){return this.operations[0]||null}},{key:"_reverseDeltaClass",get:function(){return e}}],[{key:"className",get:function(){return"engine.model.delta.MoveDelta"}},{key:"_priority",get:function(){return 20}}]),e}(uf);ln("move",function(t,e){var n=new hf;if(this.addDelta(n),t instanceof Ru){if(!t.isFlat)throw new ms("batch-move-range-not-flat: Range to move is not flat.");dn(this,n,t.start,t.end.offset-t.start.offset,e)}else dn(this,n,Cu.createBefore(t),1,e);return this}),sf.register(hf);var df=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,null,[{key:"className",get:function(){return"engine.model.delta.RemoveDelta"}}]),e}(hf);ln("remove",function(t){var e=new df;if(this.addDelta(e),t instanceof Ru){var n=t.getMinimalFlatRanges().reverse(),r=!0,i=!1,o=void 0;try{for(var a,s=n[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;vn(this,e,u.start,u.end.offset-u.start.offset)}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}}else vn(this,e,Cu.createBefore(t),1);return this}),sf.register(df);var vf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"position",get:function(){return this._insertOperation?this._insertOperation.position:null}},{key:"nodes",get:function(){return this._insertOperation?this._insertOperation.nodes:null}},{key:"_insertOperation",get:function(){return this.operations[0]||null}},{key:"_reverseDeltaClass",get:function(){return df}}],[{key:"className",get:function(){return"engine.model.delta.InsertDelta"}},{key:"_priority",get:function(){return 20}}]),e}(uf);ln("insert",function(t,e){var n=new vf,r=new tf(t,e,this.document.version);return this.addDelta(n),n.addOperation(r),this.document.applyOperation(r),this}),sf.register(vf);var yf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"getReversed",value:function(){var t=Za(Object.getPrototypeOf(e.prototype),"getReversed",this).call(this);return t.operations.length>0&&(t.operations[0].isSticky=!0),t}},{key:"position",get:function(){return this._moveOperation?this._moveOperation.sourcePosition:null}},{key:"_cloneOperation",get:function(){return this.operations[0]||null}},{key:"_moveOperation",get:function(){return this.operations[1]||null}},{key:"_reverseDeltaClass",get:function(){return mf}}],[{key:"className",get:function(){return"engine.model.delta.SplitDelta"}},{key:"_priority",get:function(){return 5}}]),e}(uf);ln("split",function(t){var e=new yf;this.addDelta(e);var n=t.parent;if(!n.parent)throw new ms("batch-split-root: Root element can not be split.");var r=new Su(n.name,n.getAttributes()),i=new tf(Cu.createAfter(n),r,this.document.version);e.addOperation(i),this.document.applyOperation(i);var o=new Qc(t,n.maxOffset-t.offset,Cu.createFromParentAndOffset(r,0),this.document.version);return o.isSticky=!0,e.addOperation(o),this.document.applyOperation(o),this}),sf.register(yf);var mf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"getReversed",value:function(){var t=Za(Object.getPrototypeOf(e.prototype),"getReversed",this).call(this);return t.operations.length>0&&(t.operations[1].isSticky=!1),t}},{key:"position",get:function(){return this._removeOperation?this._removeOperation.sourcePosition:null}},{key:"_removeOperation",get:function(){return this.operations[1]||null}},{key:"_reverseDeltaClass",get:function(){return yf}}],[{key:"className",get:function(){return"engine.model.delta.MergeDelta"}}]),e}(uf);ln("merge",function(t){var e=new mf;this.addDelta(e);var n=t.nodeBefore,r=t.nodeAfter;if(!(n instanceof Su))throw new ms("batch-merge-no-element-before: Node before merge position must be an element.");if(!(r instanceof Su))throw new ms("batch-merge-no-element-after: Node after merge position must be an element.");var i=Cu.createFromParentAndOffset(r,0),o=Cu.createFromParentAndOffset(n,n.maxOffset),a=new Qc(i,r.maxOffset,o,this.document.version);a.isSticky=!0,e.addOperation(a),this.document.applyOperation(a);var s=new Zc(t,1,this.document.version);return e.addOperation(s),this.document.applyOperation(s),this}),sf.register(mf);var pf=function(t){function e(t,n,r,i){Qa(this,e);var o=es(this,Object.getPrototypeOf(e).call(this,i));return o.position=t,o.oldName=n,o.newName=r,o}return ts(e,t),Xa(e,[{key:"clone",value:function(){return new e(Cu.createFromPosition(this.position),this.oldName,this.newName,this.baseVersion)}},{key:"getReversed",value:function(){return new e(Cu.createFromPosition(this.position),this.newName,this.oldName,this.baseVersion+1)}},{key:"_execute",value:function(){var t=this.position.nodeAfter;if(!(t instanceof Su))throw new ms("rename-operation-wrong-position: Given position is invalid or node after it is not an instance of Element.");if(t.name!==this.oldName)throw new ms("rename-operation-wrong-name: Element to change has different name than operation's old name.");return t.name=this.newName,{element:t,oldName:this.oldName}}},{key:"type",get:function(){return"rename"}}],[{key:"fromJSON",value:function(t,n){return new e(Cu.fromJSON(t.position,n),t.oldName,t.newName,t.baseVersion)}},{key:"className",get:function(){return"engine.model.operation.RenameOperation"}}]),e}(Gc),gf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"_reverseDeltaClass",get:function(){return e}}],[{key:"className",get:function(){return"engine.model.delta.RenameDelta"}}]),e}(uf);ln("rename",function(t,e){if(!(t instanceof Su))throw new ms("batch-rename-not-element-instance: Trying to rename an object which is not an instance of Element.");var n=new gf;return this.addDelta(n),yn(this,n,new pf(Cu.createBefore(t),t.name,e,this.document.version)),this}),sf.register(gf);var bf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"range",get:function(){var t=this._moveOperation;return t?Ru.createFromPositionAndShift(t.sourcePosition,t.howMany):null}},{key:"howMany",get:function(){var t=this.range;return t?t.end.offset-t.start.offset:0}},{key:"_insertOperation",get:function(){return this.operations[0]||null}},{key:"_moveOperation",get:function(){return this.operations[1]||null}},{key:"_reverseDeltaClass",get:function(){return wf}}],[{key:"className",get:function(){return"engine.model.delta.WrapDelta"}},{key:"_priority",get:function(){return 10}}]),e}(uf);ln("wrap",function(t,e){if(!t.isFlat)throw new ms("batch-wrap-range-not-flat: Range to wrap is not flat.");var n=e instanceof Su?e:new Su(e);if(n.childCount>0)throw new ms("batch-wrap-element-not-empty: Element to wrap with is not empty.");if(null!==n.parent)throw new ms("batch-wrap-element-attached: Element to wrap with is already attached to tree model.");var r=new bf;this.addDelta(r);var i=new tf(t.end,n,this.document.version);r.addOperation(i),this.document.applyOperation(i);var o=Cu.createFromParentAndOffset(n,0),a=new Qc(t.start,t.end.offset-t.start.offset,o,this.document.version);return r.addOperation(a),this.document.applyOperation(a),this}),sf.register(bf);var wf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"position",get:function(){return this._moveOperation?this._moveOperation.targetPosition:null}},{key:"_moveOperation",get:function(){return this.operations[0]||null}},{key:"_reverseDeltaClass",get:function(){return bf}}],[{key:"className",get:function(){return"engine.model.delta.UnwrapDelta"}},{key:"_priority",get:function(){return 10}}]),e}(uf);ln("unwrap",function(t){if(null===t.parent)throw new ms("batch-unwrap-element-no-parent: Trying to unwrap an element that has no parent.");var e=new wf;this.addDelta(e);var n=Cu.createFromParentAndOffset(t,0),r=new Qc(n,t.maxOffset,Cu.createBefore(t),this.document.version);r.isSticky=!0,e.addOperation(r),this.document.applyOperation(r);var i=new Zc(Cu.createBefore(t),1,this.document.version);return e.addOperation(i),this.document.applyOperation(i),this}),sf.register(wf);var kf=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,null,[{key:"className",get:function(){return"engine.model.delta.WeakInsertDelta"}}]),e}(vf);ln("weakInsert",function(t,e){var n=new kf;this.addDelta(n),e=Kt(e);var r=!0,i=!1,o=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.setAttributesTo(this.document.selection.getAttributes())}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}var c=new tf(t,e,this.document.version);return n.addOperation(c),this.document.applyOperation(c),this}),sf.register(kf);var _f="__lodash_hash_undefined__";gn.prototype.add=gn.prototype.push=mn,gn.prototype.has=pn;var xf=1,Of=2,Pf=1,Sf=2,Af="[object Boolean]",Cf="[object Date]",Tf="[object Error]",Ef="[object Map]",Rf="[object Number]",jf="[object RegExp]",Vf="[object Set]",Ff="[object String]",Nf="[object Symbol]",Mf="[object ArrayBuffer]",If="[object DataView]",Bf=Jl?Jl.prototype:void 0,Df=Bf?Bf.valueOf:void 0,Lf=2,zf="[object Arguments]",qf="[object Array]",Hf="[object Boolean]",$f="[object Date]",Kf="[object Error]",Wf="[object Function]",Jf="[object Map]",Uf="[object Number]",Gf="[object Object]",Yf="[object RegExp]",Qf="[object Set]",Xf="[object String]",Zf="[object WeakMap]",th="[object ArrayBuffer]",eh="[object DataView]",nh="[object Float32Array]",rh="[object Float64Array]",ih="[object Int8Array]",oh="[object Int16Array]",ah="[object Int32Array]",sh="[object Uint8Array]",uh="[object Uint8ClampedArray]",lh="[object Uint16Array]",ch="[object Uint32Array]",fh={};fh[nh]=fh[rh]=fh[ih]=fh[oh]=fh[ah]=fh[sh]=fh[uh]=fh[lh]=fh[ch]=!0,fh[zf]=fh[qf]=fh[th]=fh[Hf]=fh[eh]=fh[$f]=fh[Kf]=fh[Wf]=fh[Jf]=fh[Uf]=fh[Gf]=fh[Yf]=fh[Qf]=fh[Xf]=fh[Zf]=!1;var hh=Object.prototype,dh=hh.toString,vh=2,yh="[object Arguments]",mh="[object Array]",ph="[object Object]",gh=Object.prototype,bh=gh.hasOwnProperty,wh={InsertOperation:{InsertOperation:function(t,e,n){var r=t.clone();return r.position=r.position._getTransformedByInsertion(e.position,e.nodes.maxOffset,!n),[r]},AttributeOperation:Cn,RootAttributeOperation:Cn,RenameOperation:Cn,MoveOperation:function(t,e,n){var r=t.clone();return r.position=t.position._getTransformedByMove(e.sourcePosition,e.targetPosition,e.howMany,!n,e.isSticky),[r]}},AttributeOperation:{InsertOperation:function(t,e){var n=t.range._getTransformedByInsertion(e.position,e.nodes.maxOffset,!0,!1);return n.reverse().map(function(e){return new Yc(e,t.key,t.oldValue,t.newValue,t.baseVersion)})},AttributeOperation:function(t,e,n){if(t.key===e.key){var r=t.range.getDifference(e.range).map(function(e){return new Yc(e,t.key,t.oldValue,t.newValue,t.baseVersion)});if(n&&!Sn(t.newValue,e.newValue)){var i=t.range.getIntersection(e.range);null!==i&&r.push(new Yc(i,e.key,e.oldValue,t.newValue,t.baseVersion))}return 0===r.length&&r.push(new ef(t.baseVersion)),r}return[t.clone()]},RootAttributeOperation:Cn,RenameOperation:Cn,MoveOperation:function(t,e){var n=Ru.createFromPositionAndShift(e.sourcePosition,e.howMany),r=[];e instanceof Zc&&e._needsHolderElement&&t.range.root==e.targetPosition.root&&t.range.start.path[0]>=e._holderElementOffset&&(t=t.clone(),t.range.start.path[0]++,t.range.end.path[0]++);var i=Rn(t.range.getDifference(n)),o=t.range.getIntersection(n);return null!==i&&(i.start=i.start._getTransformedByDeletion(e.sourcePosition,e.howMany),i.end=i.end._getTransformedByDeletion(e.sourcePosition,e.howMany),r=i._getTransformedByInsertion(e.movedRangeStart,e.howMany,!0,!1).reverse()),null!==o&&(o.start=o.start._getCombined(e.sourcePosition,e.movedRangeStart),o.end=o.end._getCombined(e.sourcePosition,e.movedRangeStart),r.push(o)),r.map(function(e){return new Yc(e,t.key,t.oldValue,t.newValue,t.baseVersion)})}},RootAttributeOperation:{InsertOperation:Cn,AttributeOperation:Cn,RootAttributeOperation:function(t,e,n){return t.root!==e.root||t.key!==e.key||(t.newValue===e.newValue||n)&&t.newValue!==e.newValue?[t.clone()]:[new ef(t.baseVersion)]},RenameOperation:Cn,MoveOperation:Cn},RenameOperation:{InsertOperation:function(t,e){var n=t.clone();return n.position=n.position._getTransformedByInsertion(e.position,e.nodes.maxOffset,!0),[n]},AttributeOperation:Cn,RootAttributeOperation:Cn,RenameOperation:function(t,e,n){var r=t.clone();return t.position.isEqual(e.position)&&!n?[new ef(t.baseVersion)]:[r]},MoveOperation:function(t,e){var n=t.clone(),r=n.position.isEqual(e.sourcePosition);return n.position=n.position._getTransformedByMove(e.sourcePosition,e.targetPosition,e.howMany,!0,r),[n]}},MoveOperation:{InsertOperation:function(t,e,n){var r=Ru.createFromPositionAndShift(t.sourcePosition,t.howMany);r=r._getTransformedByInsertion(e.position,e.nodes.maxOffset,!1,t.isSticky)[0];var i=new t.constructor(r.start,r.end.offset-r.start.offset,t instanceof Zc?t.baseVersion:t.targetPosition._getTransformedByInsertion(e.position,e.nodes.maxOffset,!n),t instanceof Zc?void 0:t.baseVersion);return i.isSticky=t.isSticky,[i]},AttributeOperation:Cn,RootAttributeOperation:Cn,RenameOperation:Cn,MoveOperation:function(t,e,n){if(En(t,e)&&En(e,t))return[e.getReversed()];if(t instanceof Zc&&e instanceof Zc&&e._needsHolderElement){var r=t.targetPosition.path[0],i=e.targetPosition.path[0];(r>i||r==i&&n)&&(t=t.clone(),t.targetPosition.path[0]++)}t instanceof Zc&&!(e instanceof Zc)?n=!0:!(t instanceof Zc)&&e instanceof Zc&&(n=!1);var o=Ru.createFromPositionAndShift(t.sourcePosition,t.howMany),a=Ru.createFromPositionAndShift(e.sourcePosition,e.howMany),s=[],u=Rn(o.getDifference(a));u&&(u.start=u.start._getTransformedByMove(e.sourcePosition,e.targetPosition,e.howMany,!t.isSticky,!1),u.end=u.end._getTransformedByMove(e.sourcePosition,e.targetPosition,e.howMany,t.isSticky,!1),s.push(u));var l=o.getIntersection(a),c=X(t.sourcePosition.getParentPath(),e.sourcePosition.getParentPath()),f=o.containsPosition(e.targetPosition)||o.start.isEqual(e.targetPosition)&&t.isSticky||o.end.isEqual(e.targetPosition)&&t.isSticky,h=a.containsRange(o)&&(a.containsPosition(t.targetPosition)||a.start.isEqual(t.targetPosition)||a.end.isEqual(t.targetPosition));if(null!==l&&("extension"===c||"same"===c&&n||h)&&!f&&(l.start=l.start._getCombined(e.sourcePosition,e.movedRangeStart),l.end=l.end._getCombined(e.sourcePosition,e.movedRangeStart),u&&u.start.isBefore(l.start)?s.push(l):s.unshift(l)),0===s.length)return[new ef(t.baseVersion)];var d=t.targetPosition._getTransformedByMove(e.sourcePosition,e.targetPosition,e.howMany,!n,e.isSticky||h);return s.reverse().map(function(e){var n=new t.constructor(e.start,e.end.offset-e.start.offset,t instanceof Zc?t.baseVersion:d,t instanceof Zc?void 0:t.baseVersion);return n.isSticky=t.isSticky,n._holderElementOffset=t._holderElementOffset,n})}}},kh=Math.ceil,_h=Math.max,xh=200,Oh=P(function(t,e){return j(t)?Kn(t,Mn(e,1,j,!0)):[]}),Ph=1,Sh=2,Ah="[object Map]",Ch="[object Set]",Th=Yn(Ve),Eh="Expected a function";tr.Cache=Ae;var Rh=1/0,jh=Jl?Jl.prototype:void 0,Vh=jh?jh.toString:void 0,Fh=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]/g,Nh=/\\(\\)?/g,Mh=tr(function(t){var e=[];return nr(t).replace(Fh,function(t,n,r,i){e.push(r?i.replace(Nh,"$1"):n||t)}),e}),Ih=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Bh=/^\w*$/,Dh=1/0,Lh=1,zh=2,qh=P(function(t,e){var n=Q(e);return j(n)&&(n=void 0),j(t)?Kn(t,Mn(e,1,j,!0),yr(n)):[]}),Hh=P(function(t,e){var n=Q(e);return j(n)&&(n=void 0),j(t)?Kn(t,Mn(e,1,j,!0),void 0,n):[]}),$h=4294967295,Kh=1/0,Wh=Math.max,Jh=Math.min,Uh=P(function(t){var e=qn(t,Mr);return e.length&&e[0]===t[0]?Nr(e):[]}),Gh=P(function(t){var e=Q(t),n=qn(t,Mr);return e===Q(n)?e=void 0:n.pop(),n.length&&n[0]===t[0]?Nr(n,yr(e)):[]}),Yh=P(function(t){var e=Q(t),n=qn(t,Mr);return e===Q(n)?e=void 0:n.pop(),n.length&&n[0]===t[0]?Nr(n,void 0,e):[]}),Qh=Array.prototype,Xh=Qh.join,Zh=Math.max,td=Math.min,ed=Array.prototype,nd=ed.splice,rd=P(Hr),id=Array.prototype,od=id.splice,ad=P(function(t,e){e=Mn(e,1);var n=t?t.length:0,r=Wr(t,e);return Ur(t,qn(e,function(t){return g(t,n)?+t:t}).sort(Gr)),r}),sd=Array.prototype,ud=sd.reverse,ld=4294967295,cd=ld-1,fd=Math.floor,hd=Math.min,dd=4294967295,vd=dd>>>1,yd=1/0,md=Al&&1/Xe(new Al([,-0]))[1]==yd?function(t){return new Al(t)}:yi,pd=200,gd=P(function(t){return mi(Mn(t,1,j,!0))}),bd=P(function(t){var e=Q(t);return j(e)&&(e=void 0),mi(Mn(t,1,j,!0),yr(e))}),wd=P(function(t){var e=Q(t);return j(e)&&(e=void 0),mi(Mn(t,1,j,!0),void 0,e)}),kd=Math.max,_d=P(function(t,e){return j(t)?Kn(t,e):[]}),xd=P(function(t){return xi(wi(t,j))}),Od=P(function(t){var e=Q(t);return j(e)&&(e=void 0),xi(wi(t,j),yr(e))}),Pd=P(function(t){var e=Q(t);return j(e)&&(e=void 0),xi(wi(t,j),void 0,e)}),Sd=P(ki),Ad=P(function(t){var e=t.length,n=e>1?t[e-1]:void 0;return n="function"==typeof n?(t.pop(),n):void 0,_i(t,n)}),Cd={chunk:Vn,compact:Fn,concat:In,difference:Oh,differenceBy:qh,differenceWith:Hh,drop:mr,dropRight:pr,dropRightWhile:br,dropWhile:wr,fill:Or,findIndex:Sr,findLastIndex:Ar,first:Cr,flatten:Tr,flattenDeep:Er,flattenDepth:Rr,fromPairs:jr,head:Cr,indexOf:Vr,initial:Fr,intersection:Uh,intersectionBy:Gh,intersectionWith:Yh,join:Ir,last:Q,lastIndexOf:Br,nth:Lr,pull:rd,pullAll:Hr,pullAllBy:$r,pullAllWith:Kr,pullAt:ad,remove:Yr,reverse:Qr,slice:Xr,sortedIndex:ei,sortedIndexBy:ni,sortedIndexOf:ri,sortedLastIndex:ii,sortedLastIndexBy:oi,sortedLastIndexOf:ai,sortedUniq:ui,sortedUniqBy:li,tail:ci,take:fi,takeRight:hi,takeRightWhile:di,takeWhile:vi,union:gd,unionBy:bd,unionWith:wd,uniq:pi,uniqBy:gi,uniqWith:bi,unzip:ki,unzipWith:_i,without:_d,xor:xd,xorBy:Od,xorWith:Pd,zip:Sd,zipObject:Pi,zipObjectDeep:Ai,zipWith:Ad},Td=new Map;Ri(cf,kf,function(t,e,n){var r=Ei(t,e,n);return t.range.containsPosition(e.position)&&r.push(Fi(e,t)),r}),Ri(vf,mf,function(t,e,n){return t.position.isEqual(e.position)?[e.getReversed(),t.clone()]:Ei(t,e,n)}),Ri(hf,mf,function(t,e,n){var r=t.sourcePosition.root==e.position.root&&"same"===X(t.sourcePosition.getParentPath(),e.position.getParentPath()),i=t.sourcePosition.offset<=e.position.offset&&t.sourcePosition.offset+t.howMany>e.position.offset;return r&&i?[e.getReversed(),t.clone()]:Ei(t,e,n)}),Ri(mf,vf,function(t,e,n){return t.position.isEqual(e.position)?[Ni()]:Ei(t,e,n)}),Ri(mf,hf,function(t,e,n){var r=t.position.root==e.sourcePosition.root&&"same"===X(t.position.getParentPath(),e.sourcePosition.getParentPath()),i=e.sourcePosition.offset<=t.position.offset&&e.sourcePosition.offset+e.howMany>t.position.offset;return r&&i?[Ni()]:Ei(t,e,n)}),Ri(yf,yf,function(t,e,n){var r=t.position.getParentPath(),i=e.position.getParentPath();if("same"==X(r,i)){if(t.position.offset==e.position.offset)return[Ni()];if(t.position.offsete._cloneOperation.sourcePosition.offset&&o._cloneOperation.sourcePosition.offset--,[o]}var a=t.clone();return a._cloneOperation.position.offset++,a._moveOperation.sourcePosition.path[a._moveOperation.sourcePosition.path.length-2]++,a._moveOperation.targetPosition.path[a._moveOperation.targetPosition.path.length-2]++,a._moveOperation.sourcePosition.offset=t.position.offset-e.position.offset,t._cloneOperation instanceof Xc&&e._cloneOperation instanceof Xc&&t._cloneOperation.sourcePosition.offset>e._cloneOperation.sourcePosition.offset&&a._cloneOperation.sourcePosition.offset--,[a]}return Ei(t,e,n)}),Ri(yf,wf,function(t,e,n){return"same"===X(e.position.path,t.position.getParentPath())?[Ni()]:Ei(t,e,n)}),Ri(yf,bf,function(t,e,n){var r="same"===X(t.position.getParentPath(),e.range.start.getParentPath()),i=e.range.start.offset=t.position.offset;if(r&&i)return[Ni()];if("same"===X(t.position.getParentPath(),e.range.end.getShiftedBy(-1).path)){var o=t.clone(),a=Cu.createFromPosition(e.range.start);a.path.push(e.howMany-1);var s=a.getShiftedBy(1);o._cloneOperation.position=s;var u=Cu.createFromPosition(a);u.path.push(t.position.offset),o._moveOperation.sourcePosition=u;var l=Cu.createFromPosition(s);return l.path.push(0),o._moveOperation.targetPosition=l,[o]}return Ei(t,e,n)}),Ri(wf,yf,function(t,e,n){if("same"===X(t.position.path,e.position.getParentPath())){var r=[e.getReversed(),t.clone()];return r[1].operations[1].targetPosition.path[0]++,r}return Ei(t,e,n)}),Ri(kf,cf,function(t,e,n){var r=Ei(t,e,n);return e.range.containsPosition(t.position)&&r.push(Fi(t,e)),r}),Ri(bf,yf,function(t,e,n){var r="same"===X(t.range.start.getParentPath(),e.position.getParentPath()),i=t.range.start.offset=e.position.offset;if(r&&i)return[e.getReversed(),t.clone()];if("same"===X(e.position.getParentPath(),t.range.end.getShiftedBy(-1).path)){var o=t.clone();return o._insertOperation.position.offset++,o._moveOperation.howMany++,o._moveOperation.targetPosition.path[o._moveOperation.targetPosition.path.length-2]++,[o]}return Ei(t,e,n)});var Ed=function(){function t(){Qa(this,t),this._deltas=[],this._historyPoints=new Map;
+}return Xa(t,[{key:"addDelta",value:function(t){if(t.operations.length>0&&!this._historyPoints.has(t.baseVersion)){var e=this._deltas.length;this._deltas[e]=t,this._historyPoints.set(t.baseVersion,e)}}},{key:"getDeltas",value:regeneratorRuntime.mark(function e(){var t,n,r=arguments.length<=0||void 0===arguments[0]?0:arguments[0],i=arguments.length<=1||void 0===arguments[1]?Number.POSITIVE_INFINITY:arguments[1];return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(0!==this._deltas.length){e.next=2;break}return e.abrupt("return");case 2:if(t=this._getIndex(r),t!=-1){e.next=5;break}return e.abrupt("return");case 5:if(!(t=i)){e.next=9;break}return e.abrupt("break",13);case 9:return e.next=11,n;case 11:e.next=5;break;case 13:case"end":return e.stop()}},e,this)})},{key:"getDelta",value:function(t){var e=this._historyPoints.get(t);if(void 0===e)return null;var n=[];for(e;et&&this._historyPoints.set(p,this._historyPoints.get(p)+f)}}catch(c){d=!0,v=c}finally{try{!h&&m["return"]&&m["return"]()}finally{if(d)throw v}}}}},{key:"_getIndex",value:function(t){var e=this._historyPoints.get(t);if(void 0===e){var n=this._deltas[this._deltas.length-1],r=n.baseVersion+n.operations.length;if(t<0||t>=r)return-1;throw new ms("model-history-wrong-version: Given base version points to the middle of a delta.")}return e}}]),t}(),Rd=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n));return Mi.call(r),r}return ts(e,t),Xa(e,[{key:"detach",value:function(){this.stopListening()}}]),e}(Ru);K(Rd,ss);var jd=function(){function t(){Qa(this,t),this._lastRangeBackward=!1,this._ranges=[],this._attrs=new Map}return Xa(t,[{key:"isEqual",value:function(t){var e=this.rangeCount;if(e!=t.rangeCount)return!1;for(var n=0;n0&&(this._removeAllRanges(),this.fire("change:range",{directChange:!0}))}},{key:"setRanges",value:function(t){var e=this,n=!(arguments.length<=1||void 0===arguments[1])&&arguments[1];t=Array.from(t);var r=t.some(function(t){if(!(t instanceof Ru))throw new ms("model-selection-added-not-range: Trying to add an object that is not an instance of Range.");return e._ranges.every(function(e){return!e.isEqual(t)})});if(t.length!==this._ranges.length||r){this._removeAllRanges();var i=!0,o=!1,a=void 0;try{for(var s,u=t[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;this._pushRange(l)}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}this._lastRangeBackward=!!n,this.fire("change:range",{directChange:!0})}}},{key:"setTo",value:function(t){this.setRanges(t.getRanges(),t.isBackward)}},{key:"collapse",value:function(t,e){var n=Cu.createAt(t,e),r=new Ru(n,n);this.setRanges([r])}},{key:"collapseToStart",value:function(){var t=this.getFirstPosition();null!==t&&this.setRanges([new Ru(t,t)])}},{key:"collapseToEnd",value:function(){var t=this.getLastPosition();null!==t&&this.setRanges([new Ru(t,t)])}},{key:"setFocus",value:function(t,e){if(null===this.anchor)throw new ms("model-selection-setFocus-no-ranges: Cannot set selection focus if there are no ranges in selection.");var n=Cu.createAt(t,e);if("same"!=n.compareWith(this.focus)){var r=this.anchor;this._ranges.length&&this._popRange(),"before"==n.compareWith(r)?this.addRange(new Ru(n,r),!0):this.addRange(new Ru(r,n))}}},{key:"getAttribute",value:function(t){return this._attrs.get(t)}},{key:"getAttributes",value:function(){return this._attrs.entries()}},{key:"getAttributeKeys",value:function(){return this._attrs.keys()}},{key:"hasAttribute",value:function(t){return this._attrs.has(t)}},{key:"clearAttributes",value:function(){if(this._attrs.size>0){var t=Array.from(this._attrs.keys());this._attrs.clear(),this.fire("change:attribute",{attributeKeys:t,directChange:!0})}}},{key:"removeAttribute",value:function(t){this.hasAttribute(t)&&(this._attrs["delete"](t),this.fire("change:attribute",{attributeKeys:[t],directChange:!0}))}},{key:"setAttribute",value:function(t,e){this.getAttribute(t)!==e&&(this._attrs.set(t,e),this.fire("change:attribute",{attributeKeys:[t],directChange:!0}))}},{key:"setAttributesTo",value:function(t){if(t=J(t),!Bi(t,this._attrs)){var e=new Set(Array.from(t.keys()).concat(Array.from(this._attrs.keys()))),n=!0,r=!1,i=void 0;try{for(var o,a=t[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=ns(o.value,2),u=s[0],l=s[1];this._attrs.get(u)===l&&e["delete"](u)}}catch(c){r=!0,i=c}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}this._attrs=t,this.fire("change:attribute",{attributeKeys:Array.from(e),directChange:!0})}}},{key:"_pushRange",value:function(t){if(!(t instanceof Ru))throw new ms("model-selection-added-not-range: Trying to add an object that is not an instance of Range.");this._checkRange(t),this._ranges.push(Ru.createFromRange(t))}},{key:"_checkRange",value:function(t){for(var e=0;e0;)this._popRange()}},{key:"anchor",get:function(){if(this._ranges.length>0){var t=this._ranges[this._ranges.length-1];return this._lastRangeBackward?t.end:t.start}return null}},{key:"focus",get:function(){if(this._ranges.length>0){var t=this._ranges[this._ranges.length-1];return this._lastRangeBackward?t.start:t.end}return null}},{key:"isCollapsed",get:function(){var t=this._ranges.length;return 1===t&&this._ranges[0].isCollapsed}},{key:"rangeCount",get:function(){return this._ranges.length}},{key:"isBackward",get:function(){return!this.isCollapsed&&this._lastRangeBackward}}],[{key:"createFromSelection",value:function(t){var e=new this;return e.setTo(t),e}}]),t}();K(jd,ss);var Vd="selection:",Fd=new Set(["addAttribute","removeAttribute","changeAttribute","addRootAttribute","removeRootAttribute","changeRootAttribute"]),Nd=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this));return n._document=t,n._attributePriority=new Map,n.listenTo(n._document,"change",function(t,e){Fd.has(e)&&n._updateAttributes(!1)}),n}return ts(e,t),Xa(e,[{key:"destroy",value:function(){for(var t=0;t0){var n=Array.from(e);this.fire("change:attribute",{attributeKeys:n,directChange:!0})}}},{key:"clearAttributes",value:function(){this.setAttributesTo([])}},{key:"refreshAttributes",value:function(){this._updateAttributes(!0)}},{key:"_popRange",value:function(){this._ranges.pop().detach()}},{key:"_pushRange",value:function(t){var e=this._prepareRange(t);e&&this._ranges.push(e)}},{key:"_prepareRange",value:function(t){var e=this;if(!(t instanceof Ru))throw new ms("model-selection-added-not-range: Trying to add an object that is not an instance of Range.");if(t.root==this._document.graveyard)return void wu.warn("model-selection-range-in-graveyard: Trying to add a Range that is in the graveyard root. Range rejected.");this._checkRange(t);var n=Rd.createFromRange(t);return this.listenTo(n,"change",function(t,r){n.root==e._document.graveyard&&e._fixGraveyardSelection(n,r),e.fire("change:range",{directChange:!1})}),n}},{key:"_updateAttributes",value:function(t){var e=J(this._getSurroundingAttributes()),n=J(this.getAttributes());if(t)this._attributePriority=new Map,this._attrs=new Map;else{var r=!0,i=!1,o=void 0;try{for(var a,s=this._attributePriority[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=ns(a.value,2),l=u[0],c=u[1];"low"==c&&(this._attrs["delete"](l),this._attributePriority["delete"](l))}}catch(f){i=!0,o=f}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}}this._setAttributesTo(e,!1);var h=[],d=!0,v=!1,y=void 0;try{for(var m,p=this.getAttributes()[Symbol.iterator]();!(d=(m=p.next()).done);d=!0){var g=ns(m.value,2),b=g[0],w=g[1];n.has(b)&&n.get(b)===w||h.push(b)}}catch(f){v=!0,y=f}finally{try{!d&&p["return"]&&p["return"]()}finally{if(v)throw y}}var k=!0,_=!1,x=void 0;try{for(var O,P=n[Symbol.iterator]();!(k=(O=P.next()).done);k=!0){var S=ns(O.value,1),A=S[0];this.hasAttribute(A)||h.push(A)}}catch(f){_=!0,x=f}finally{try{!k&&P["return"]&&P["return"]()}finally{if(_)throw x}}h.length>0&&this.fire("change:attribute",{attributeKeys:h,directChange:!1})}},{key:"_setAttribute",value:function(t,n){var r=arguments.length<=2||void 0===arguments[2]||arguments[2],i=r?"normal":"low";if("low"==i&&"normal"==this._attributePriority.get(t))return!1;var o=Za(Object.getPrototypeOf(e.prototype),"getAttribute",this).call(this,t);return o!==n&&(this._attrs.set(t,n),this._attributePriority.set(t,i),!0)}},{key:"_removeAttribute",value:function(t){var n=arguments.length<=1||void 0===arguments[1]||arguments[1],r=n?"normal":"low";return("low"!=r||"normal"!=this._attributePriority.get(t))&&(!!Za(Object.getPrototypeOf(e.prototype),"hasAttribute",this).call(this,t)&&(this._attrs["delete"](t),this._attributePriority.set(t,r),!0))}},{key:"_setAttributesTo",value:function(t){var e=arguments.length<=1||void 0===arguments[1]||arguments[1],n=new Set,r=!0,i=!1,o=void 0;try{for(var a,s=this.getAttributes()[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=ns(a.value,2),l=u[0],c=u[1];t.get(l)!==c&&this._removeAttribute(l,e)&&n.add(l)}}catch(f){i=!0,o=f}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}var h=!0,d=!1,v=void 0;try{for(var y,m=t[Symbol.iterator]();!(h=(y=m.next()).done);h=!0){var p=ns(y.value,2),g=p[0],b=p[1],w=this._setAttribute(g,b,e);w&&n.add(g)}}catch(f){d=!0,v=f}finally{try{!h&&m["return"]&&m["return"]()}finally{if(d)throw v}}return n}},{key:"_getStoredAttributes",value:regeneratorRuntime.mark(function r(){var t,e,n,i,o,a,s,u;return regeneratorRuntime.wrap(function(r){for(;;)switch(r.prev=r.next){case 0:if(t=this.getFirstPosition().parent,!this.isCollapsed||0!==t.childCount){r.next=30;break}e=!0,n=!1,i=void 0,r.prev=5,o=t.getAttributeKeys()[Symbol.iterator]();case 7:if(e=(a=o.next()).done){r.next=16;break}if(s=a.value,0!==s.indexOf(Vd)){r.next=13;break}return u=s.substr(Vd.length),r.next=13,[u,t.getAttribute(s)];case 13:e=!0,r.next=7;break;case 16:r.next=22;break;case 18:r.prev=18,r.t0=r["catch"](5),n=!0,i=r.t0;case 22:r.prev=22,r.prev=23,!e&&o["return"]&&o["return"]();case 25:if(r.prev=25,!n){r.next=28;break}throw i;case 28:return r.finish(25);case 29:return r.finish(22);case 30:case"end":return r.stop()}},r,this,[[5,18,22,30],[23,,25,29]])})},{key:"_removeStoredAttribute",value:function(t){var n=e._getStoreAttributeKey(t);this._document.batch().removeAttribute(this.anchor.parent,n)}},{key:"_storeAttribute",value:function(t,n){var r=e._getStoreAttributeKey(t);this._document.batch().setAttribute(this.anchor.parent,r,n)}},{key:"_setStoredAttributesTo",value:function(t){var n=this.anchor.parent,r=this._document.batch(),i=!0,o=!1,a=void 0;try{for(var s,u=this._getStoredAttributes()[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=ns(s.value,1),c=l[0],f=e._getStoreAttributeKey(c);r.removeAttribute(n,f)}}catch(h){o=!0,a=h}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}var d=!0,v=!1,y=void 0;try{for(var m,p=t[Symbol.iterator]();!(d=(m=p.next()).done);d=!0){var g=ns(m.value,2),b=g[0],w=g[1],k=e._getStoreAttributeKey(b);r.setAttribute(n,k,w)}}catch(h){v=!0,y=h}finally{try{!d&&p["return"]&&p["return"]()}finally{if(v)throw y}}}},{key:"_getSurroundingAttributes",value:function(){var t=this.getFirstPosition(),e=null;if(this.isCollapsed){var n=t.textNode?t.textNode:t.nodeBefore,r=t.textNode?t.textNode:t.nodeAfter;if(e=Di(n),e||(e=Di(r)),!e)for(var i=n;i&&!e;)i=i.previousSibling,e=Di(i);if(!e)for(var o=r;o&&!e;)o=o.nextSibling,e=Di(o);e||(e=this._getStoredAttributes())}else{var a=this.getFirstRange(),s=!0,u=!1,l=void 0;try{for(var c,f=a[Symbol.iterator]();!(s=(c=f.next()).done);s=!0){var h=c.value;"text"==h.type&&null===e&&(e=h.item.getAttributes())}}catch(d){u=!0,l=d}finally{try{!s&&f["return"]&&f["return"]()}finally{if(u)throw l}}}return e}},{key:"_fixGraveyardSelection",value:function(t,e){var n=t.start.path,r=e.start.path.length-(n.length-2),i=e.start.path.slice(0,r);i[i.length-1]-=n[1];var o=new Cu(e.root,i),a=this._prepareRange(new Ru(o,o)),s=this._ranges.indexOf(t);this._ranges.splice(s,1,a),t.detach()}},{key:"isCollapsed",get:function(){var t=this._ranges.length;return 0===t||Za(Object.getPrototypeOf(e.prototype),"isCollapsed",this)}},{key:"anchor",get:function(){return Za(Object.getPrototypeOf(e.prototype),"anchor",this)||this._document._getDefaultRange().start}},{key:"focus",get:function(){return Za(Object.getPrototypeOf(e.prototype),"focus",this)||this._document._getDefaultRange().start}},{key:"rangeCount",get:function(){return this._ranges.length?this._ranges.length:1}}],[{key:"createFromSelection",value:function(t){var e=new this(t._document);return e.setTo(t),e}},{key:"_getStoreAttributeKey",value:function(t){return Vd+t}}]),e}(jd),Md=function(){function t(e){Qa(this,t),this._schema=e,this._allowed=[],this._disallowed=[],this._requiredAttributes=[]}return Xa(t,[{key:"allow",value:function(t,e){this._addPath("_allowed",t,e)}},{key:"disallow",value:function(t,e){this._addPath("_disallowed",t,e)}},{key:"requireAttributes",value:function(t){this._requiredAttributes.push(t)}},{key:"_addPath",value:function(t,e,n){e=e.slice(),ou(n)||(n=[n]);var r=!0,i=!1,o=void 0;try{for(var a,s=n[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;this[t].push({path:e,attribute:u})}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}}},{key:"_getPaths",value:function(t,e){var n="allow"===t?this._allowed:this._disallowed,r=[],i=!0,o=!1,a=void 0;try{for(var s,u=n[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;l.attribute===e&&r.push(l.path)}}catch(c){o=!0,a=c}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}return r}},{key:"_checkRequiredAttributes",value:function(t){var e=!0,n=!0,r=!1,i=void 0;try{for(var o,a=this._requiredAttributes[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;e=!0;var u=!0,l=!1,c=void 0;try{for(var f,h=s[Symbol.iterator]();!(u=(f=h.next()).done);u=!0){var d=f.value;if(t.indexOf(d)==-1){e=!1;break}}}catch(v){l=!0,c=v}finally{try{!u&&h["return"]&&h["return"]()}finally{if(l)throw c}}if(e)break}}catch(v){r=!0,i=v}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}return e}},{key:"_hasMatchingPath",value:function(t,e,n){var r=this._getPaths(t,n),i=!0,o=!1,a=void 0;try{for(var s,u=r[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value,c=0,f=!0,h=!1,d=void 0;try{for(var v,y=e[Symbol.iterator]();!(f=(v=y.next()).done);f=!0){var m=v.value;if(this._schema.hasItem(m)){var p=this._schema._extensionChains.get(m);p.indexOf(l[c])>-1&&c++}}}catch(g){h=!0,d=g}finally{try{!f&&y["return"]&&y["return"]()}finally{if(h)throw d}}if(c===l.length)return!0}}catch(g){o=!0,a=g}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}return!1}},{key:"toJSON",value:function(){var t=un(this);return t._schema="[model.Schema]",t}}]),t}(),Id=function(){function t(){Qa(this,t),this.objects=new Set,this._items=new Map,this._extensionChains=new Map,this.registerItem("$root"),this.registerItem("$block"),this.registerItem("$inline"),this.registerItem("$text","$inline"),this.allow({name:"$block",inside:"$root"}),this.allow({name:"$inline",inside:"$block"}),this.registerItem("$clipboardHolder","$root"),this.allow({name:"$inline",inside:"$clipboardHolder"})}return Xa(t,[{key:"allow",value:function(e){this._getItem(e.name).allow(t._normalizeQueryPath(e.inside),e.attributes)}},{key:"disallow",value:function(e){this._getItem(e.name).disallow(t._normalizeQueryPath(e.inside),e.attributes)}},{key:"requireAttributes",value:function(t,e){this._getItem(t).requireAttributes(e)}},{key:"check",value:function(e){var n=this;if(!this.hasItem(e.name))return!1;ou(e.attributes)?0===e.attributes.length&&e.attributes.push(void 0):e.attributes=[e.attributes];var r=t._normalizeQueryPath(e.inside),i=this._extensionChains.get(e.name).map(function(t){return n._getItem(t)});if(!this._getItem(e.name)._checkRequiredAttributes(e.attributes))return!1;var o=!0,a=!1,s=void 0;try{for(var u,l=e.attributes[Symbol.iterator]();!(o=(u=l.next()).done);o=!0){var c=u.value,f=!0,h=!1,d=void 0;try{for(var v,y=i[Symbol.iterator]();!(f=(v=y.next()).done);f=!0){var m=v.value;if(m._hasMatchingPath("disallow",r,c))return!1}}catch(p){h=!0,d=p}finally{try{!f&&y["return"]&&y["return"]()}finally{if(h)throw d}}}}catch(p){a=!0,s=p}finally{try{!o&&l["return"]&&l["return"]()}finally{if(a)throw s}}var g=!0,b=!1,w=void 0;try{for(var k,_=e.attributes[Symbol.iterator]();!(g=(k=_.next()).done);g=!0){var x=k.value,O=!1,P=!0,S=!1,A=void 0;try{for(var C,T=i[Symbol.iterator]();!(P=(C=T.next()).done);P=!0){var E=C.value;if(E._hasMatchingPath("allow",r,x)){O=!0;break}}}catch(p){S=!0,A=p}finally{try{!P&&T["return"]&&T["return"]()}finally{if(S)throw A}}if(!O)return!1}}catch(p){b=!0,w=p}finally{try{!g&&_["return"]&&_["return"]()}finally{if(b)throw w}}return!0}},{key:"hasItem",value:function(t){return this._items.has(t)}},{key:"registerItem",value:function(t,e){if(this.hasItem(t))throw new ms("model-schema-item-exists: Item with specified name already exists in schema.");if(e&&!this.hasItem(e))throw new ms("model-schema-no-item: Item with specified name does not exist in schema.");this._items.set(t,new Md(this));var n=this.hasItem(e)?this._extensionChains.get(e).concat(t):[t];this._extensionChains.set(t,n)}},{key:"itemExtends",value:function(t,e){if(!this.hasItem(t)||!this.hasItem(e))throw new ms("model-schema-no-item: Item with specified name does not exist in schema.");var n=this._extensionChains.get(t);return n.some(function(t){return t==e})}},{key:"_getItem",value:function(t){if(!this.hasItem(t))throw new ms("model-schema-no-item: Item with specified name does not exist in schema.");return this._items.get(t)}}],[{key:"_normalizeQueryPath",value:function(t){var e=[];if(ou(t)){var n=!0,r=!1,i=void 0;try{for(var o,a=t[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;s instanceof Su?e.push(s.name):F(s)&&e.push(s)}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}}else if(t instanceof Cu){for(var l=t.parent;null!==l;)e.push(l.name),l=l.parent;e.reverse()}else F(t)&&(e=t.split(" "));return e}}]),t}(),Bd=function(){function t(){Qa(this,t),this.on("deleteContents",function(t,e){return Li(e.batch,e.selection,e.options)}),this.on("modifySelection",function(t,e){return Ji(e.selection,e.options)})}return Xa(t,[{key:"deleteContents",value:function(t,e,n){this.fire("deleteContents",{batch:t,selection:e,options:n})}},{key:"modifySelection",value:function(t,e){this.fire("modifySelection",{selection:t,options:e})}}]),t}();K(Bd,ss);var Dd="$graveyard",Ld=function(){function t(){var e=this;Qa(this,t),this.version=0,this.selection=new Nd(this),this.schema=new Id,this.history=new Ed(this),this.composer=new Bd,this._pendingChanges=[],this._roots=new Map,this.selection.on("change:range",function(){var t=!0,n=!1,r=void 0;try{for(var i,o=e.selection.getRanges()[Symbol.iterator]();!(t=(i=o.next()).done);t=!0){var a=i.value;if(!e._validateSelectionRange(a))throw new ms("document-selection-wrong-position: Range from document selection starts or ends at incorrect position.",{range:a})}}catch(s){n=!0,r=s}finally{try{!t&&o["return"]&&o["return"]()}finally{if(n)throw r}}}),this.createRoot("$root",Dd)}return Xa(t,[{key:"applyOperation",value:function(t){if(t.baseVersion!==this.version)throw new ms("model-document-applyOperation-wrong-version: Only operations with matching versions can be applied.",{operation:t});var e=t._execute();this.version++,this.history.addDelta(t.delta);var n=t.delta&&t.delta.batch;e&&this.fire("change",t.type,e,n)}},{key:"batch",value:function(t){return new lf(this,t)}},{key:"createRoot",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"$root":arguments[0],e=arguments.length<=1||void 0===arguments[1]?"main":arguments[1];if(this._roots.has(e))throw new ms("model-document-createRoot-name-exists: Root with specified name already exists.",{name:e});var n=new Xu(this,t,e);return this._roots.set(e,n),n}},{key:"destroy",value:function(){this.selection.destroy(),this.stopListening()}},{key:"enqueueChanges",value:function(t){if(this._pendingChanges.push(t),1==this._pendingChanges.length){for(;this._pendingChanges.length;)this._pendingChanges[0](),this._pendingChanges.shift();this.fire("changesDone")}}},{key:"getRoot",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"main":arguments[0];if(!this._roots.has(t))throw new ms("model-document-getRoot-root-not-exist: Root with specified name does not exist.",{name:t});return this._roots.get(t)}},{key:"hasRoot",value:function(t){return this._roots.has(t)}},{key:"getRootNames",value:function(){return Array.from(this._roots.keys()).filter(function(t){return t!=Dd})}},{key:"toJSON",value:function(){var t=un(this);return t.selection="[engine.model.LiveSelection]",t}},{key:"_getDefaultRoot",value:function(){var t=!0,e=!1,n=void 0;try{for(var r,i=this._roots.values()[Symbol.iterator]();!(t=(r=i.next()).done);t=!0){var o=r.value;if(o!==this.graveyard)return o}}catch(a){e=!0,n=a}finally{try{!t&&i["return"]&&i["return"]()}finally{if(e)throw n}}return this.graveyard}},{key:"_getDefaultRange",value:function(){var t=this._getDefaultRoot(),e=!0,n=!1,r=void 0;try{for(var i,o=Ru.createIn(t).getPositions()[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;if(this.schema.check({name:"$text",inside:a}))return new Ru(a,a)}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}var u=new Cu(t,[0]);return new Ru(u,u)}},{key:"_validateSelectionRange",value:function(t){return Yi(t.start)&&Yi(t.end)}},{key:"graveyard",get:function(){return this.getRoot(Dd)}}]),t}();K(Ld,ss);var zd=vu({},ss,{listenTo:function(){var t=Array.prototype.slice.call(arguments),e=t[0];Xi(e)&&(t[0]=this._getProxyEmitter(e)||new qd(e)),ss.listenTo.apply(this,t)},stopListening:function(){var t=Array.prototype.slice.call(arguments),e=t[0];if(Xi(e)){var n=this._getProxyEmitter(e);if(!n)return;t[0]=n}ss.stopListening.apply(this,t)},_getProxyEmitter:function(t){var e=void 0,n=void 0,r=void 0,i=Qi(t);return(n=this._listeningTo)&&(r=n[i])&&(e=r.emitter),e||null}}),qd=function Tm(t){Qa(this,Tm),this._emitterId=Qi(t),this._domNode=t};vu(qd.prototype,ss,{on:function(t,e){var n=arguments.length<=2||void 0===arguments[2]?{}:arguments[2];if(ss.on.apply(this,arguments),!this._domListeners||!this._domListeners[t]){var r=this._createDomListener(t);this._domNode.addEventListener(t,r,!!n.useCapture),this._domListeners||(this._domListeners={}),this._domListeners[t]=r}},off:function(t){ss.off.apply(this,arguments);var e=void 0;!this._domListeners[t]||(e=this._events[t])&&e.callbacks.length||this._domListeners[t].removeListener()},_createDomListener:function(t){var e=this,n=function(n){e.fire(t,n)};return n.removeListener=function(){e._domNode.removeEventListener(t,n),delete e._domListeners[t]},n}});var Hd=function(){function t(){Qa(this,t),this.set("isFocused",!1),this._elements=new Set,this._nextEventLoopTimeout=null,this._focusedElement=null}return Xa(t,[{key:"add",value:function(t){var e=this;if(this._elements.has(t))throw new ms("focusTracker-add-element-already-exist");this.listenTo(t,"focus",function(){return e._focus(t)},{useCapture:!0}),this.listenTo(t,"blur",function(){return e._blur()},{useCapture:!0}),this._elements.add(t)}},{key:"remove",value:function(t){t===this._focusedElement&&this._blur(t),this._elements.has(t)&&(this.stopListening(t),this._elements["delete"](t))}},{key:"_focus",value:function(t){clearTimeout(this._nextEventLoopTimeout),this._focusedElement=t,this.isFocused=!0}},{key:"_blur",value:function(){var t=this;this._nextEventLoopTimeout=setTimeout(function(){t._focusedElement=null,t.isFocused=!1},0)}}]),t}();K(Hd,zd),K(Hd,gu);var $d=function(){function t(e){Qa(this,t),this.config=new ys(e),this.plugins=new ku(this),this.commands=new Map,this.locale=new _u(this.config.get("lang")),this.t=this.locale.t,this.document=new Ld,this.data=new el(this.document),this.focusTracker=new Hd}return Xa(t,[{key:"initPlugins",value:function e(){function t(){var t=r.get("features")||[];return n.plugins.load(t)}function e(t){return t.reduce(function(t,e){return t.then(e.init.bind(e))},Promise.resolve())}var n=this,r=this.config;return t().then(e)}},{key:"destroy",value:function(){var t=this;return this.fire("destroy"),this.stopListening(),Promise.resolve().then(function(){t.document.destroy(),t.data.destroy()})}},{key:"execute",value:function(t,e){var n=this.commands.get(t);if(!n)throw new ms("editor-command-not-found: Specified command has not been added to the editor.");n._execute(e)}}],[{key:"create",value:function(t){var e=this;return new Promise(function(n){var r=new e(t);n(r.initPlugins().then(function(){return r}))})}}]),t}();K($d,ss);var Kd=navigator.userAgent.toLowerCase(),Wd={mac:Zi(Kd)},Jd=ro(),Ud=function(){function t(e){var n=this;Qa(this,t),this.editor=e,this._listener=Object.create(ss),this._keystrokes=new Map,this._listener.listenTo(e.editing.view,"keydown",function(t,e){var r=n.press(e);r&&e.preventDefault()})}return Xa(t,[{key:"set",value:function(t,e){this._keystrokes.set(eo(t),e)}},{key:"press",value:function(t){var e=to(t),n=this._keystrokes.get(e);return!!n&&("string"==typeof n?this.editor.execute(n):n(t),!0)}},{key:"destroy",value:function(){this._keystrokes=new Map,this._listener.stopListening()}}]),t}(),Gd=function(){function t(){Qa(this,t),this._ranges=[],this._lastRangeBackward=!1,this._isFake=!1,this._fakeSelectionLabel=""}return Xa(t,[{key:"setFake",value:function(){var t=arguments.length<=0||void 0===arguments[0]||arguments[0],e=arguments.length<=1||void 0===arguments[1]?{}:arguments[1];this._isFake=t,this._fakeSelectionLabel=t?e.label||"":"",this.fire("change")}},{key:"addRange",value:function(t,e){if(!(t instanceof Lu))throw new ms("view-selection-invalid-range: Invalid Range.");this._pushRange(t),this._lastRangeBackward=!!e,this.fire("change")}},{key:"getRanges",value:regeneratorRuntime.mark(function e(){var t,n,r,i,o,a;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:t=!0,n=!1,r=void 0,e.prev=3,i=this._ranges[Symbol.iterator]();case 5:if(t=(o=i.next()).done){e.next=12;break}return a=o.value,e.next=9,Lu.createFromRange(a);case 9:t=!0,e.next=5;break;case 12:e.next=18;break;case 14:e.prev=14,e.t0=e["catch"](3),n=!0,r=e.t0;case 18:e.prev=18,e.prev=19,!t&&i["return"]&&i["return"]();case 21:if(e.prev=21,!n){e.next=24;break}throw r;case 24:return e.finish(21);case 25:return e.finish(18);case 26:case"end":return e.stop()}},e,this,[[3,14,18,26],[19,,21,25]])})},{key:"getFirstRange",value:function(){var t=null,e=!0,n=!1,r=void 0;try{for(var i,o=this._ranges[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;t&&!a.start.isBefore(t.start)||(t=a)}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}return t?Lu.createFromRange(t):null}},{key:"getLastRange",value:function(){var t=null,e=!0,n=!1,r=void 0;try{for(var i,o=this._ranges[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;t&&!a.end.isAfter(t.end)||(t=a)}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}return t?Lu.createFromRange(t):null}},{key:"getFirstPosition",value:function(){var t=this.getFirstRange();
+return t?Bu.createFromPosition(t.start):null}},{key:"getLastPosition",value:function(){var t=this.getLastRange();return t?Bu.createFromPosition(t.end):null}},{key:"isEqual",value:function(t){var e=this.rangeCount;if(e!=t.rangeCount)return!1;if(this.isFake!=t.isFake)return!1;if(this.isFake&&this.fakeSelectionLabel!=t.fakeSelectionLabel)return!1;for(var n=0;n=0;a--)r.setAttribute(o[a].name,o[a].value)}if(e.withChildren||void 0===e.withChildren){var s=!0,u=!1,l=void 0;try{for(var c,f=this.domChildrenToView(t,e)[Symbol.iterator]();!(s=(c=f.next()).done);s=!0){var h=c.value;r.appendChildren(h)}}catch(d){u=!0,l=d}finally{try{!s&&f["return"]&&f["return"]()}finally{if(u)throw l}}}return r}},{key:"domChildrenToView",value:regeneratorRuntime.mark(function n(t){var e,r,i,o=arguments.length<=1||void 0===arguments[1]?{}:arguments[1];return regeneratorRuntime.wrap(function(n){for(;;)switch(n.prev=n.next){case 0:e=0;case 1:if(!(e0){var s=!1;i?" "!=i.data.charAt(0)&&"Â "!=i.data.charAt(0)||o.length%2===0&&(s=!0):o.length%2&&(s=!0),s&&(o="Â "+o.substr(0,o.length-1)),o=o.replace(/ /g," Â ")}return a+o}},{key:"_getTouchingViewTextNode",value:function(t,e){if(!t.parent)return null;var n=new Du({startPosition:e?Bu.createAfter(t):Bu.createBefore(t),direction:e?"forward":"backward"}),r=!0,i=!1,o=void 0;try{for(var a,s=n[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;if(u.item instanceof Mu)return null;if(u.item instanceof Vu)return u.item}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}return null}},{key:"_processDataFromDomText",value:function(t){var e=so(t);if(go(t,this.preElements))return e;e=e.replace(/[^\S\u00A0]{2,}/g," ");var n=this._getTouchingDomTextNode(t,!1),r=this._getTouchingDomTextNode(t,!0);return n&&!/[^\S\u00A0]/.test(n.data.charAt(n.data.length-1))||(e=e.replace(/^ /,"")),r||(e=e.replace(/ $/,"")),e=e.replace(/ \u00A0/g," "),n&&!/[^\S\u00A0]/.test(n.data.charAt(n.data.length-1))||(e=e.replace(/^\u00A0/," ")),r&&"Â "!=r.data.charAt(0)||(e=e.replace(/\u00A0( *)$/," $1")),e}},{key:"_getTouchingDomTextNode",value:function(t,e){if(!t.parentNode)return null;var n=e?"nextNode":"previousNode",r=t.ownerDocument,i=r.createTreeWalker(r.childNodes[0],NodeFilter.SHOW_TEXT);i.currentNode=t;var o=i[n]();if(null!==o){var a=po(t,o);if(a&&!go(t,this.blockElements,a)&&!go(o,this.blockElements,a))return o}return null}}]),t}(),iv=function(t){function e(t,n){var r=arguments.length<=2||void 0===arguments[2]?"main":arguments[2];Qa(this,e);var i=es(this,Object.getPrototypeOf(e).call(this,t,n));return i.rootName=r,i}return ts(e,t),e}(Iu),ov=function(){function t(e){Qa(this,t),this.document=e,this.isEnabled=!1}return Xa(t,[{key:"enable",value:function(){this.isEnabled=!0}},{key:"disable",value:function(){this.isEnabled=!1}}]),t}(),av=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n._config={childList:!0,characterData:!0,characterDataOldValue:!0,subtree:!0},n.domConverter=t.domConverter,n.renderer=t.renderer,n._domElements=[],n._mutationObserver=new window.MutationObserver(n._onMutations.bind(n)),n}return ts(e,t),Xa(e,[{key:"flush",value:function(){this._onMutations(this._mutationObserver.takeRecords())}},{key:"observe",value:function(t){this._domElements.push(t),this.isEnabled&&this._mutationObserver.observe(t,this._config)}},{key:"enable",value:function(){Za(Object.getPrototypeOf(e.prototype),"enable",this).call(this);var t=!0,n=!1,r=void 0;try{for(var i,o=this._domElements[Symbol.iterator]();!(t=(i=o.next()).done);t=!0){var a=i.value;this._mutationObserver.observe(a,this._config)}}catch(s){n=!0,r=s}finally{try{!t&&o["return"]&&o["return"]()}finally{if(n)throw r}}}},{key:"disable",value:function(){Za(Object.getPrototypeOf(e.prototype),"disable",this).call(this),this._mutationObserver.disconnect()}},{key:"_onMutations",value:function(t){if(0!==t.length){var e=this.domConverter,n=new Map,r=new Set,i=!0,o=!1,a=void 0;try{for(var s,u=t[Symbol.iterator]();!(i=(s=u.next()).done);i=!0){var l=s.value;if("childList"===l.type){var c=e.getCorrespondingViewElement(l.target);c&&r.add(c)}}}catch(f){o=!0,a=f}finally{try{!i&&u["return"]&&u["return"]()}finally{if(o)throw a}}var h=!0,d=!1,v=void 0;try{for(var y,m=t[Symbol.iterator]();!(h=(y=m.next()).done);h=!0){var p=y.value;if("characterData"===p.type){var g=e.getCorrespondingViewText(p.target);g&&!r.has(g.parent)?n.set(g,{type:"text",oldText:g.data,newText:so(p.target),node:g}):!g&&oo(p.target)&&r.add(e.getCorrespondingViewElement(p.target.parentNode))}}}catch(f){d=!0,v=f}finally{try{!h&&m["return"]&&m["return"]()}finally{if(d)throw v}}var b=[],w=!0,k=!1,_=void 0;try{for(var x,O=n.values()[Symbol.iterator]();!(w=(x=O.next()).done);w=!0){var P=x.value;this.renderer.markToSync("text",P.node),b.push(P)}}catch(f){k=!0,_=f}finally{try{!w&&O["return"]&&O["return"]()}finally{if(k)throw _}}var S=!0,A=!1,C=void 0;try{for(var T,E=r[Symbol.iterator]();!(S=(T=E.next()).done);S=!0){var R=T.value,j=e.getCorrespondingDomElement(R),V=R.getChildren(),F=e.domChildrenToView(j);this.renderer.markToSync("children",R),b.push({type:"children",oldChildren:Array.from(V),newChildren:Array.from(F),node:R})}}catch(f){A=!0,C=f}finally{try{!S&&E["return"]&&E["return"]()}finally{if(A)throw C}}var N=t[0].target.ownerDocument.getSelection(),M=null;if(N&&N.anchorNode){var I=e.domPositionToView(N.anchorNode,N.anchorOffset),B=e.domPositionToView(N.focusNode,N.focusOffset);I&&B&&(M=new Gd,M.collapse(I),M.setFocus(B))}this.document.fire("mutations",b,M),this.document.render()}}}]),e}(ov),sv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n.mutationObserver=t.getObserver(av),n.document=t,n.selection=t.selection,n.domConverter=t.domConverter,n._documents=new WeakSet,n}return ts(e,t),Xa(e,[{key:"observe",value:function(t){var e=this,n=t.ownerDocument;this._documents.has(n)||(n.addEventListener("selectionchange",function(){return e._handleSelectionChange(n)}),this._documents.add(n))}},{key:"_handleSelectionChange",value:function(t){if(this.isEnabled&&this.document.isFocused){this.mutationObserver.flush();var e=t.defaultView.getSelection(),n=this.domConverter.domSelectionToView(e);if(!this.selection.isEqual(n)){if(this._isInfiniteLoop(n))return void wu.warn("selectionchange-infinite-loop: Selection change observer detected an infinite rendering loop.");this.document.fire("selectionChange",{oldSelection:this.selection,newSelection:n,domSelection:e}),this.document.render()}}}},{key:"_isInfiniteLoop",value:function(t){return this._lastSelection&&this._lastButOneSelection&&(t.isEqual(this._lastSelection)||t.isEqual(this._lastButOneSelection))?this._loopbackCounter++:(this._lastButOneSelection=this._lastSelection,this._lastSelection=t,this._loopbackCounter=0),this._loopbackCounter>10}}]),e}(ov),uv=function(){function t(e,n,r){Qa(this,t),this.document=e,this.domEvent=n,this.domTarget=n.target,vu(this,r)}return Xa(t,[{key:"preventDefault",value:function(){this.domEvent.preventDefault()}},{key:"stopPropagation",value:function(){this.domEvent.stopPropagation()}},{key:"target",get:function(){return this.document.domConverter.getCorrespondingViewElement(this.domTarget)}}]),t}(),lv=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"observe",value:function(t){var e=this,n="string"==typeof this.domEventType?[this.domEventType]:this.domEventType;n.forEach(function(n){t.addEventListener(n,function(t){return e.isEnabled&&e.onDomEvent(t)})})}},{key:"fire",value:function(t,e,n){this.isEnabled&&this.document.fire(t,new uv(this.document,e,n))}}]),e}(ov),cv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n.domEventType=["focus","blur"],t.on("focus",function(){t.isFocused=!0}),t.on("blur",function(e,n){var r=t.selection.editableElement;null!==r&&r!==n.target||(t.isFocused=!1)}),n}return ts(e,t),Xa(e,[{key:"onDomEvent",value:function(t){this.fire(t.type,t)}}]),e}(lv),fv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n.domEventType="keydown",n}return ts(e,t),Xa(e,[{key:"onDomEvent",value:function(t){this.fire("keydown",t,{keyCode:t.keyCode,altKey:t.altKey,ctrlKey:t.ctrlKey||t.metaKey,shiftKey:t.shiftKey,get keystroke(){return to(this)}})}}]),e}(lv),hv=function(t){function e(t){return Qa(this,e),es(this,Object.getPrototypeOf(e).call(this,t))}return ts(e,t),Xa(e,[{key:"observe",value:function(){var t=this,e=this.document;e.on("keydown",function(n,r){var i=e.selection;i.isFake&&bo(r.keyCode)&&t.isEnabled&&(r.preventDefault(),t._handleSelectionMove(r.keyCode))},{priority:"lowest"})}},{key:"_handleSelectionMove",value:function(t){var e=this.document.selection,n=Gd.createFromSelection(e);n.setFake(!1),t!=Jd.arrowleft&&t!=Jd.arrowup||n.collapseToStart(),t!=Jd.arrowright&&t!=Jd.arrowdown||n.collapseToEnd(),this.document.fire("selectionChange",{oldSelection:e,newSelection:n,domSelection:null})}}]),e}(ov),dv=function(){function t(){var e=this;Qa(this,t),this.domRoots=new Map,this.selection=new Gd,this.domConverter=new rv,this.roots=new Map,this.set("isFocused",!1),this.renderer=new nv(this.domConverter,this.selection),this.renderer.bind("isFocused").to(this,"isFocused"),this._observers=new Map,this.addObserver(av),this.addObserver(sv),this.addObserver(cv),this.addObserver(fv),this.addObserver(hv),lo(this),this.on("render",function(){e.disableObservers(),e.renderer.render(),e.enableObservers()})}return Xa(t,[{key:"addObserver",value:function(t){var e=this._observers.get(t);if(e)return e;e=new t(this),this._observers.set(t,e);var n=!0,r=!1,i=void 0;try{for(var o,a=this.domRoots[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=ns(o.value,2),u=s[0],l=s[1];e.observe(l,u)}}catch(c){r=!0,i=c}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}return e.enable(),e}},{key:"getObserver",value:function(t){return this._observers.get(t)}},{key:"createRoot",value:function(t){var e=this,n=arguments.length<=1||void 0===arguments[1]?"main":arguments[1],r="string"==typeof t?t:t.tagName,i=new iv(this,r.toLowerCase(),n);return this.roots.set(n,i),i.on("change:children",function(t,n){return e.renderer.markToSync("children",n)}),i.on("change:attributes",function(t,n){return e.renderer.markToSync("attributes",n)}),i.on("change:text",function(t,n){return e.renderer.markToSync("text",n)}),this.domConverter.isElement(t)&&this.attachDomRoot(t,n),i}},{key:"attachDomRoot",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?"main":arguments[1],n=this.getRoot(e);this.domRoots.set(e,t),this.domConverter.bindElements(t,n),this.renderer.markToSync("children",n),this.renderer.domDocuments.add(t.ownerDocument);var r=!0,i=!1,o=void 0;try{for(var a,s=this._observers.values()[Symbol.iterator]();!(r=(a=s.next()).done);r=!0){var u=a.value;u.observe(t,e)}}catch(l){i=!0,o=l}finally{try{!r&&s["return"]&&s["return"]()}finally{if(i)throw o}}}},{key:"getRoot",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"main":arguments[0];return this.roots.get(t)}},{key:"getDomRoot",value:function(){var t=arguments.length<=0||void 0===arguments[0]?"main":arguments[0];return this.domRoots.get(t)}},{key:"render",value:function(){this.fire("render")}},{key:"focus",value:function(){if(!this.isFocused){var t=this.selection.editableElement;t?(this.domConverter.focus(t),this.render()):wu.warn("view-focus-no-selection: There is no selection in any editable to focus.")}}},{key:"disableObservers",value:function(){var t=!0,e=!1,n=void 0;try{for(var r,i=this._observers.values()[Symbol.iterator]();!(t=(r=i.next()).done);t=!0){var o=r.value;o.disable()}}catch(a){e=!0,n=a}finally{try{!t&&i["return"]&&i["return"]()}finally{if(e)throw n}}}},{key:"enableObservers",value:function(){var t=!0,e=!1,n=void 0;try{for(var r,i=this._observers.values()[Symbol.iterator]();!(t=(r=i.next()).done);t=!0){var o=r.value;o.enable()}}catch(a){e=!0,n=a}finally{try{!t&&i["return"]&&i["return"]()}finally{if(e)throw n}}}}]),t}();K(dv,gu);var vv=function(){function t(e){var n=this;Qa(this,t),this.model=e,this.view=new dv,this.mapper=new zu,this.modelToView=new Hu({mapper:this.mapper,viewSelection:this.view.selection}),this._listenter=Object.create(ss),this._listenter.listenTo(this.model,"change",function(t,e,r){n.modelToView.convertChange(e,r)},{priority:"low"}),this._listenter.listenTo(this.model,"changesDone",function(){n.modelToView.convertSelection(e.selection),n.view.render()},{priority:"low"}),this._listenter.listenTo(this.view,"selectionChange",wo(e,this.mapper)),this.modelToView.on("insert:$text",Et(),{priority:"lowest"}),this.modelToView.on("remove",Mt(),{priority:"low"}),this.modelToView.on("move",Nt(),{priority:"low"}),this.modelToView.on("rename",Bt(),{priority:"low"}),this.modelToView.on("selection",Oo(),{priority:"low"}),this.modelToView.on("selection",Po(),{priority:"low"}),this.modelToView.on("selection",ko(),{priority:"low"}),this.modelToView.on("selection",_o(),{priority:"low"})}return Xa(t,[{key:"createRoot",value:function(t){var e=arguments.length<=1||void 0===arguments[1]?"main":arguments[1],n=this.view.createRoot(t,e),r=this.model.getRoot(e);return this.mapper.bindElements(r,n),n}},{key:"destroy",value:function(){this._listenter.stopListening()}}]),t}(),yv=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,n));return r.element=t,r.editing=new vv(r.document),r.keystrokes=new Ud(r),r}return ts(e,t),Xa(e,[{key:"destroy",value:function(){var t=this;return Promise.resolve().then(function(){return t.editing.destroy()}).then(Za(Object.getPrototypeOf(e.prototype),"destroy",this).call(this))}},{key:"setData",value:function(t){this.data.set(t)}},{key:"getData",value:function(){return this.data.get()}},{key:"updateEditorElement",value:function(){Ao(this.element,this.getData())}},{key:"loadDataFromEditorElement",value:function(){this.setData(So(this.element))}}],[{key:"create",value:function(t,e){var n=this;return new Promise(function(r){var i=new n(t,e);r(i.initPlugins().then(function(){return i}))})}}]),e}($d),mv=function(){function t(){Qa(this,t)}return Xa(t,[{key:"getHtml",value:function(t){var e=document.implementation.createHTMLDocument(""),n=e.createElement("div");return n.appendChild(t),n.innerHTML}}]),t}(),pv=function(){function t(){Qa(this,t),this._domParser=new DOMParser,this._domConverter=new rv({blockFiller:Qd}),this._htmlWriter=new mv}return Xa(t,[{key:"toData",value:function(t){var e=this._domConverter.viewToDom(t,document);
+return this._htmlWriter.getHtml(e)}},{key:"toView",value:function(t){var e=this._toDom(t);return this._domConverter.domToView(e)}},{key:"_toDom",value:function(t){for(var e=this._domParser.parseFromString(t,"text/html"),n=e.createDocumentFragment(),r=e.body.childNodes;r.length>0;)n.appendChild(r[0]);return n}}]),t}(),gv=function(){function t(e){Qa(this,t),this._items=[],this._itemMap=new Map,this._idProperty=e&&e.idProperty||"id"}return Xa(t,[{key:"add",value:function(t,e){var n=void 0,r=this._idProperty;if(r in t){if(n=t[r],"string"!=typeof n)throw new ms("collection-add-invalid-id");if(this.get(n))throw new ms("collection-add-item-already-exists")}else n=this._getNextId(),t[r]=n;if(void 0===e)e=this._items.length;else if(e>this._items.length||e<0)throw new ms("collection-add-item-invalid-index");return this._items.splice(e,0,t),this._itemMap.set(n,t),this.fire("add",t,e),this}},{key:"get",value:function(t){var e=void 0;if("string"==typeof t)e=this._itemMap.get(t);else{if("number"!=typeof t)throw new ms("collection-get-invalid-arg: Index or id must be given.");e=this._items[t]}return e||null}},{key:"remove",value:function(t){var e=void 0,n=void 0,r=void 0,i=!1,o=this._idProperty;if("string"==typeof t?(n=t,r=this._itemMap.get(n),i=!r,r&&(e=this._items.indexOf(r))):"number"==typeof t?(e=t,r=this._items[e],i=!r,r&&(n=r[o])):(r=t,n=r[o],e=this._items.indexOf(r),i=e==-1||!this._itemMap.get(n)),i)throw new ms("collection-remove-404: Item not found.");return this._items.splice(e,1),this._itemMap["delete"](n),this.fire("remove",r),r}},{key:"map",value:function(t,e){return this._items.map(t,e)}},{key:"find",value:function(t,e){return this._items.find(t,e)}},{key:"filter",value:function(t,e){return this._items.filter(t,e)}},{key:"clear",value:function(){for(;this.length;)this.remove(0)}},{key:Symbol.iterator,value:function(){return this._items[Symbol.iterator]()}},{key:"_getNextId",value:function(){var t=void 0;do t=String(os());while(this._itemMap.has(t));return t}},{key:"length",get:function(){return this._items.length}}]),t}();K(gv,ss);var bv=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this));if(!t)throw new ms("ui-controllercollection-no-name: ControllerCollection must be initialized with a name.");return r.name=t,r.locale=n,r.parent=null,r}return ts(e,t),Xa(e,[{key:"add",value:function(t,n){Za(Object.getPrototypeOf(e.prototype),"add",this).call(this,t,n);var r=Promise.resolve();return this.parent&&this.parent.ready&&!t.ready&&(r=r.then(function(){return t.init()})),r}},{key:"bind",value:function(t){function e(t){return{add:function(e,r){var a=t(e,o.locale);i.set(e,a),a&&o.add(a,"number"==typeof r?n(r):void 0)},remove:function(t){var e=i.get(t);i["delete"](e),e&&o.remove(e)}}}function n(e){for(var n=e,r=0;r'+this.sprite+"";for(var n=t.firstChild.childNodes,r=0;ralign-center align-left align-right bold bold bulletedlist image italic italic link numberedlist picker quote redo source table underline undo unlink '}),Iv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this));return n.editor=t,n.featureComponents=new _v(t),n.addCollection("body"),n}return ts(e,t),Xa(e,[{key:"init",value:function(){return this._setupIconManager(),Za(Object.getPrototypeOf(e.prototype),"init",this).call(this)}},{key:"_setupIconManager",value:function(){this.icons=Mv.icons,this.collections.get("body").add(new xv(Mv,new jv))}}]),e}(kv);K(Iv,gu);var Bv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));n.addCollection("top"),n.addCollection("main");var r=t.config;return n.set("width",r.get("ui.width")),n.set("height",r.get("ui.height")),n}return ts(e,t),e}(Iv),Dv=function(t){function e(t,n,r){Qa(this,e);var i=es(this,Object.getPrototypeOf(e).call(this,t,n));return i.editor=r,n.bind("isReadOnly","isFocused").to(t),n.set("name",t.rootName),i}return ts(e,t),e}(kv),Lv=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t)),i=r.bindTemplate;return n&&(r.element=r.editableElement=n),r.template=new Sv({tag:"div",attributes:{"class":[i.to("isFocused",function(t){return t?"ck-focused":"ck-blurred"}),"ck-editor__editable"],contenteditable:i.to("isReadOnly",function(t){return!t})}}),r}return ts(e,t),Xa(e,[{key:"init",value:function(){this.editableElement?this.template.apply(this.editableElement):this.editableElement=this.element,Za(Object.getPrototypeOf(e.prototype),"init",this).call(this)}},{key:"destroy",value:function(){Za(Object.getPrototypeOf(e.prototype),"destroy",this).call(this),this.editableElement.contentEditable=!1}}]),e}(Ev),zv=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n)),i=r.bindTemplate,o=r.t,a=function(t){return o("Rich Text Editor, %0",[t])};return Sv.extend(r.template,{attributes:{role:"textbox","aria-label":i.to("name",a),title:i.to("name",a),"class":"ck-editor__editable_inline"}}),r}return ts(e,t),e}(Lv),qv={bindToolbarItems:function(){var t=this;if(this.items=new gv,this.model.config){var e=!0,n=!1,r=void 0;try{for(var i,o=this.model.config[Symbol.iterator]();!(e=(i=o.next()).done);e=!0){var a=i.value;this.items.add({name:a})}}catch(s){n=!0,r=s}finally{try{!e&&o["return"]&&o["return"]()}finally{if(n)throw r}}}this.collections.get("items").bind(this.items).as(function(e){var n=e.name;return t.editor.ui.featureComponents.create(n)})}},Hv=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n));return r.addCollection("items"),r}return ts(e,t),e}(kv),$v=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n));return n.bind("isActive","limiterElement").to(t),r}return ts(e,t),e}(Hv),Kv=function(t){function e(t,n,r){Qa(this,e);var i=es(this,Object.getPrototypeOf(e).call(this,t,n));return i.editor=r,i}return ts(e,t),Xa(e,[{key:"init",value:function(){return this.bindToolbarItems(),Za(Object.getPrototypeOf(e.prototype),"init",this).call(this)}}]),e}($v);K(Kv,qv);var Wv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n.template=new Sv({tag:"div",attributes:{"class":["ck-toolbar"]}}),n.register("items",function(t){return t}),n}return ts(e,t),e}(Ev),Jv=Yo("px"),Uv=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t)),r=n.bindTemplate;return n.set("isSticky",!1),n.set("limiterElement",null),n.set("limiterOffset",50),n.set("_left",null),n.set("_marginLeft",null),n.set("_isStickyToTheLimiter",!1),Sv.extend(n.template,{attributes:{"class":[r["if"]("isSticky","ck-toolbar_sticky"),r["if"]("_isStickyToTheLimiter","ck-toolbar_sticky_bottom-limit")],style:{width:r.to("isSticky",function(t){if(t){var e=window.getComputedStyle(n.element);return Jv(n._elementPlaceholder.getBoundingClientRect().width+parseFloat(e.borderLeftWidth)+parseFloat(e.borderRightWidth))}return null}),top:r.to("_isStickyToTheLimiter",function(t){return t?Jv(window.scrollY+n._limiterRect.bottom-n._toolbarRect.height-n.limiterOffset):null}),left:r.to("_left"),marginLeft:r.to("_marginLeft")}}}),n._elementPlaceholder=new Sv({tag:"div",attributes:{"class":["ck-toolbar__placeholder"],style:{display:r.to("isSticky",function(t){return t?"block":"none"}),height:r.to("isSticky",function(t){return t?Jv(n._toolbarRect.height):null})}}}).render(),n}return ts(e,t),Xa(e,[{key:"init",value:function(){var t=this;Za(Object.getPrototypeOf(e.prototype),"init",this).call(this),this.element.parentNode.insertBefore(this._elementPlaceholder,this.element),this.listenTo(window,"scroll",function(){t._checkIfShouldBeSticky()}),this.listenTo(this,"change:isActive",function(){t._checkIfShouldBeSticky()})}},{key:"destroy",value:function(){Za(Object.getPrototypeOf(e.prototype),"destroy",this).call(this),this._elementPlaceholder.remove()}},{key:"_checkIfShouldBeSticky",value:function(){var t=this._limiterRect=this.limiterElement.getBoundingClientRect(),e=this._toolbarRect=this.element.getBoundingClientRect();this.isSticky=this.isActive&&t.top<0&&this._toolbarRect.height+this.limiterOffset0?t:null}},{key:"getElementName",value:function(){return 1!=this._patterns.length||!this._patterns[0].name||this._patterns[0].name instanceof RegExp?null:this._patterns[0].name}}]),t}(),ry=function(){function t(){Qa(this,t),this._dispatchers=[],this._from=[]}return Xa(t,[{key:"for",value:function(){for(var t=arguments.length,e=Array(t),n=0;n=a&&u>o&&(i=s,o=u,a=l),o===n}),this.arrow=i,this.top=t[i].top,this.left=t[i].left}}]),e}(Ev),Uy=function(){function t(e){Qa(this,t),Object.assign(this,ba(e))}return Xa(t,[{key:"clone",value:function(){return new t(this)}},{key:"moveTo",value:function(t){var e=t.top,n=t.left;return this.top=e,this.right=n+this.width,this.bottom=e+this.height,this.left=n,this}},{key:"getIntersectArea",value:function(t){var e=Math.max(0,Math.min(this.right,t.right)-Math.max(this.left,t.left)),n=Math.max(0,Math.min(this.bottom,t.bottom)-Math.max(this.top,t.top));return e*n}}]),t}(),Gy=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n));return n.bind("text","for").to(t),r}return ts(e,t),e}(kv),Yy=function(t){function e(t,n,r,i){Qa(this,e);var o=es(this,Object.getPrototypeOf(e).call(this,t,n));return o._uid="ck-input-"+os(),o.label=o._createLabel(),o.input=o._spawnInput(r,i),o}return ts(e,t),Xa(e,[{key:"_createLabel",value:function(){var t=new Vv;return t.bind("text").to(this.model,"label"),t.set("for",this._uid),new Gy(t,this.view.labelView)}},{key:"_spawnInput",value:function(t,e){return e.bind("value").to(this.model,"value"),e.set("id",this._uid),new t(e,this.view.inputView)}},{key:"value",get:function(){return this.input.value}}]),e}(kv),Qy=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n));return n.bind("value","id").to(t),r}return ts(e,t),Xa(e,[{key:"value",get:function(){return this.view.element.value}}]),e}(kv),Xy=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t,n)),i=r.view.t,o=new Vv({label:i("Link URL")});return o.bind("value").to(t,"url"),r.urlInput=new Yy(o,n.urlInputView,Qy,new Vv),r.saveButton=new cy(new Vv({isEnabled:!0,isOn:!1,label:i("Save"),withText:!0,type:"submit"}),n.saveButtonView),r.cancelButton=new cy(new Vv({isEnabled:!0,isOn:!1,label:i("Cancel"),withText:!0}),n.cancelButtonView),r.unlinkButton=new cy(new Vv({isEnabled:!0,isOn:!1,label:i("Unlink"),icon:"unlink"}),n.unlinkButtonView),n.delegate("submit").to(t),r.listenTo(r.cancelButton.model,"execute",function(){r.model.fire("cancel")}),r.listenTo(r.unlinkButton.model,"execute",function(){r.model.fire("unlink")}),r.add(r.urlInput),r.add(r.saveButton),r.add(r.cancelButton),r.add(r.unlinkButton),r}return ts(e,t),e}(kv),Zy=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t)),r=n.bindTemplate;return n.template=new Sv({tag:"label",attributes:{"class":["ck-label"],"for":r.to("for")},children:[{text:r.to("text")}]}),n}return ts(e,t),e}(Ev),tm=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,n));return r.labelView=new Zy(r.locale),r.inputView=new t(r.locale),r.template=new Sv({tag:"div",children:[r.labelView,r.inputView]}),r}return ts(e,t),Xa(e,[{key:"select",value:function(){this.inputView.select()}}]),e}(Ev),em=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t)),r=n.bindTemplate;return n.template=new Sv({tag:"input",attributes:{type:"text","class":["ck-input","ck-input-text"],id:r.to("id")}}),n.on("change:value",function(t,e,r){return n.element.value=r||""}),n}return ts(e,t),Xa(e,[{key:"select",value:function(){this.element.select()}}]),e}(Ev),nm=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n.urlInputView=new tm(em,t),n.saveButtonView=new fy(t),n.cancelButtonView=new fy(t),n.unlinkButtonView=new fy(t),Sv.extend(n.saveButtonView.template,{attributes:{"class":["ck-button-action"]}}),n.template=new Sv({tag:"form",attributes:{"class":["ck-link-form"]},children:[n.urlInputView,{tag:"div",attributes:{"class":["ck-link-form__actions"]
+},children:[n.saveButtonView,n.cancelButtonView,n.unlinkButtonView]}]}),_a({view:n}),n}return ts(e,t),e}(Ev),rm=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"init",value:function(){this.editor.editing.view.addObserver(By),this.balloonPanel=this._createBalloonPanel(),this.form=this._createForm(),this._createToolbarLinkButton(),this._createToolbarUnlinkButton()}},{key:"_createToolbarLinkButton",value:function(){var t=this,e=this.editor,n=e.commands.get("link"),r=e.t,i=new Vv({isEnabled:!0,isOn:!1,label:r("Link"),icon:"link",keystroke:"CTRL+K"});i.bind("isEnabled").to(n,"isEnabled"),this.listenTo(i,"execute",function(){return t._showPanel()}),e.ui.featureComponents.add("link",cy,fy,i),e.keystrokes.set("CTRL+K",function(){return t._showPanel()})}},{key:"_createToolbarUnlinkButton",value:function(){var t=this.editor,e=t.t,n=t.commands.get("unlink"),r=new Vv({isEnabled:!1,isOn:!1,label:e("Unlink"),icon:"unlink"});r.bind("isEnabled").to(n,"isEnabled"),this.listenTo(r,"execute",function(){t.execute("unlink")}),t.ui.featureComponents.add("unlink",cy,fy,r)}},{key:"_createBalloonPanel",value:function(){var t=this,e=this.editor,n=e.editing.view,r=new Hy(new Vv({maxWidth:300}),new Jy(e.locale));return e.focusTracker.add(r.view.element),this.listenTo(n,"click",function(){var e=n.selection,i=xa(e.getFirstPosition());e.isCollapsed&&i&&(t._attachPanelToElement(),t.listenTo(n,"render",function(){var n=xa(e.getFirstPosition());e.isCollapsed&&i===n?t._attachPanelToElement(i):t._hidePanel()}),t.listenTo(r.view,"change:isVisible",function(){return t.stopListening(n,"render")}))}),pa({controller:r.view,model:r.view,activeIf:"isVisible",callback:function(){return t._hidePanel(!0)}}),ya({controller:r.view,model:r.view,activeIf:"isVisible",contextElement:r.view.element,callback:function(){return t._hidePanel()}}),e.ui.add("body",r),r}},{key:"_createForm",value:function(){var t=this,e=this.editor,n=new Vv;n.bind("url").to(e.commands.get("link"),"value");var r=new Xy(n,new nm(e.locale));return this.listenTo(n,"submit",function(){e.execute("link",t.form.urlInput.value),t._hidePanel(!0)}),this.listenTo(n,"unlink",function(){e.execute("unlink"),t._hidePanel(!0)}),this.listenTo(n,"cancel",function(){return t._hidePanel(!0)}),this.balloonPanel.add("content",r),r}},{key:"_attachPanelToElement",value:function(t){var e=this.editor.editing.view,n=e.domConverter.getCorrespondingDomElement(e.selection.editableElement),r=t||xa(e.selection.getFirstPosition());r?this.balloonPanel.view.attachTo(e.domConverter.getCorrespondingDomElement(r),n):this.balloonPanel.view.attachTo(e.domConverter.viewRangeToDom(e.selection.getFirstRange()),n)}},{key:"_hidePanel",value:function(t){this.balloonPanel.view.hide(),t&&this.editor.editing.view.focus()}},{key:"_showPanel",value:function(){this._attachPanelToElement(),this.form.urlInput.view.select()}}],[{key:"requires",get:function(){return[qy]}}]),e}(ty),im=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t));r.type="bulleted"==n?"bulleted":"numbered",r.set("value",!1);var i=function(){r.refreshValue(),r.refreshState()};return r.listenTo(t.document.selection,"change:range",i),r.listenTo(t.document,"changesDone",i),r}return ts(e,t),Xa(e,[{key:"refreshValue",value:function(){var t=this.editor.document.selection.getFirstPosition(),e=Oa(t);this.value=null!==e&&e.getAttribute("type")==this.type}},{key:"_doExecute",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],n=this.editor.document,r=Pa(n.selection,n.schema),i=this.value===!0;n.enqueueChanges(function(){var o=e.batch||n.batch();if(i){for(var a=r[r.length-1].nextSibling,s=Number.POSITIVE_INFINITY,u=[];a&&"listItem"==a.name&&0!==a.getAttribute("indent");){var l=a.getAttribute("indent");le;)i.push(o),o=o.nextSibling;t._indentBy<0&&(i=i.reverse());var a=!0,s=!1,u=void 0;try{for(var l,c=i[Symbol.iterator]();!(a=(l=c.next()).done);a=!0){var f=l.value,h=f.getAttribute("indent")+t._indentBy;h<0?n.rename(f,"paragraph").removeAttribute(f,"indent").removeAttribute(f,"type"):n.setAttribute(f,"indent",h)}}catch(d){s=!0,u=d}finally{try{!a&&c["return"]&&c["return"]()}finally{if(s)throw u}}})}},{key:"_checkEnabled",value:function(){var t=Oa(this.editor.document.selection.getFirstPosition());if(!t)return!1;var e=t.previousSibling,n=t.getAttribute("indent"),r=n+this._indentBy;if(this._indentBy>0){if(!e||"listItem"!=e.name)return!1;if(e.getAttribute("indent")+1=this.limit&&this._reset()}},{key:"destroy",value:function(){this.document.off("change",this._changeCallback)}},{key:"_onBatch",value:function(t){"transparent"!=t.type&&t!==this._batch&&qa(t.getOperations())<=1&&this._reset()}},{key:"_reset",value:function(){this._batch=null,this.size=0}},{key:"batch",get:function(){return this._batch||(this._batch=this.document.batch()),this._batch}}]),t}(),cm=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"init",value:function(){var t=this,e=this.editor,n=e.editing.view;this._buffer=new lm(e.document,e.config.get("typing.undoStep")||20),this.listenTo(n,"keydown",function(e,n){t._handleKeydown(n)},{priority:"lowest"}),this.listenTo(n,"mutations",function(e,n,r){t._handleMutations(n,r)})}},{key:"destroy",value:function(){Za(Object.getPrototypeOf(e.prototype),"destroy",this).call(this),this._buffer.destroy(),this._buffer=null}},{key:"_handleKeydown",value:function(t){var e=this,n=this.editor.document;$a(t)||n.selection.isCollapsed||n.enqueueChanges(function(){n.composer.deleteContents(e._buffer.batch,n.selection)})}},{key:"_handleMutations",value:function(t,e){var n=this.editor.document,r=new fm(this.editor.editing,this._buffer);n.enqueueChanges(function(){return r.handle(t,e)})}}]),e}(ty),fm=function(){function t(e,n){Qa(this,t),this.editing=e,this.buffer=n,this.insertedCharacterCount=0}return Xa(t,[{key:"handle",value:function(t,e){var n=!0,r=!1,i=void 0;try{for(var o,a=t[Symbol.iterator]();!(n=(o=a.next()).done);n=!0){var s=o.value;this._handleTextMutation(s,e),this._handleTextNodeInsertion(s)}}catch(u){r=!0,i=u}finally{try{!n&&a["return"]&&a["return"]()}finally{if(r)throw i}}this.buffer.input(Math.max(this.insertedCharacterCount,0))}},{key:"_handleTextMutation",value:function(t,e){if("text"==t.type){for(var n=t.newText.replace(/\u00A0/g," "),r=t.oldText.replace(/\u00A0/g," "),i=fo(r,n),o=null,a=null,s=0;s0){var y=Ru.createFromPositionAndShift(v,l);this._remove(y,l)}var m=t.newText.substr(o,c);this._insert(v,m),h&&this.editing.model.selection.collapse(h)}}},{key:"_handleTextNodeInsertion",value:function(t){if("children"==t.type&&t.newChildren.length-t.oldChildren.length==1){var e=fo(t.oldChildren,t.newChildren,Ka),n=Ha(e,t.newChildren);if(!(n.length>1)){var r=n[0];if(r.values[0]instanceof Vu){var i=new Bu(t.node,r.index),o=this.editing.mapper.toModelPosition(i),a=r.values[0].data;a=a.replace(/\u00A0/g," "),this._insert(o,a),this.editing.model.selection.collapse(o.parent,"end")}}}}},{key:"_insert",value:function(t,e){this.buffer.batch.weakInsert(t,e),this.insertedCharacterCount+=e.length}},{key:"_remove",value:function(t,e){this.buffer.batch.remove(t),this.insertedCharacterCount-=e}}]),t}(),hm=[to("arrowUp"),to("arrowRight"),to("arrowDown"),to("arrowLeft"),16,17,18,20,27,33,34,35,36],dm=112;dm<=135;dm++)hm.push(dm);var vm=function(t){function e(t,n){Qa(this,e);var r=es(this,Object.getPrototypeOf(e).call(this,t));return r.direction=n,r._buffer=new lm(t.document,t.config.get("undo.step")),r}return ts(e,t),Xa(e,[{key:"_doExecute",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],n=this.editor.document;n.enqueueChanges(function(){var r=jd.createFromSelection(n.selection);if(r.isCollapsed&&n.composer.modifySelection(r,{direction:t.direction,unit:e.unit}),!r.isCollapsed){var i=0;r.getFirstRange().getMinimalFlatRanges().forEach(function(t){i+=qa(t.getWalker({singleCharacters:!0,ignoreElementEnd:!0,shallow:!0}))}),n.composer.deleteContents(t._buffer.batch,r,{merge:!0}),t._buffer.input(i),n.selection.setRanges(r.getRanges(),r.isBackward)}})}}]),e}(iy),ym=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return t.on("keydown",function(e,n){var r={};if(n.keyCode==Jd["delete"])r.direction="forward",r.unit="character";else{if(n.keyCode!=Jd.backspace)return;r.direction="backward",r.unit="codePoint"}r.unit=n.altKey?"word":r.unit,t.fire("delete",new uv(t,n.domEvent,r))}),n}return ts(e,t),Xa(e,[{key:"observe",value:function(){}}]),e}(ov),mm=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"init",value:function(){var t=this.editor,e=t.editing.view;e.addObserver(ym),t.commands.set("forwardDelete",new vm(t,"forward")),t.commands.set("delete",new vm(t,"backward")),this.listenTo(e,"delete",function(e,n){t.execute("forward"==n.direction?"forwardDelete":"delete",{unit:n.unit}),n.preventDefault()})}}]),e}(ty),pm=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,null,[{key:"requires",get:function(){return[cm,mm]}}]),e}(ty),gm=function(t){function e(t){Qa(this,e);var n=es(this,Object.getPrototypeOf(e).call(this,t));return n._stack=[],n._createdBatches=new WeakSet,n.refreshState(),n}return ts(e,t),Xa(e,[{key:"addBatch",value:function(t){var e={ranges:Array.from(this.editor.document.selection.getRanges()),isBackward:this.editor.document.selection.isBackward};this._stack.push({batch:t,selection:e}),this.refreshState()}},{key:"clearStack",value:function(){this._stack=[],this.refreshState()}},{key:"_checkEnabled",value:function(){return this._stack.length>0}},{key:"_restoreSelection",value:function(t,e,n){var r=this.editor.document,i=[],o=!0,a=!1,s=void 0;try{for(var u,l=t[Symbol.iterator]();!(o=(u=l.next()).done);o=!0){var c=u.value,f=Ja(c,n),h=f.find(function(t){return t.start.root!=r.graveyard});h&&i.push(h)}}catch(d){a=!0,s=d}finally{try{!o&&l["return"]&&l["return"]()}finally{if(a)throw s}}i.length&&r.selection.setRanges(i,e)}}]),e}(iy),bm=function(t){function e(){return Qa(this,e),es(this,Object.getPrototypeOf(e).apply(this,arguments))}return ts(e,t),Xa(e,[{key:"_doExecute",value:function(){var t=this,e=arguments.length<=0||void 0===arguments[0]?null:arguments[0],n=e?this._stack.findIndex(function(t){return t.batch==e}):this._stack.length-1,r=this._stack.splice(n,1)[0];this.editor.document.enqueueChanges(function(){var e=t._undo(r.batch),n=t.editor.document.history.getDeltas(r.batch.baseVersion);t._restoreSelection(r.selection.ranges,r.selection.isBackward,n),t.fire("revert",r.batch,e)}),this.refreshState()}},{key:"_getItemIndexFromBaseVersion",value:function(t){for(var e=0;e