text: /**
   * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
   * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
   */
  /* globals console, window, document */
  
  import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
  import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
  import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  import Heading from '@ckeditor/ckeditor5-heading/src/heading';
  import List from '@ckeditor/ckeditor5-list/src/list';
  import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
  import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
  
  import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  import { toWidget, viewToModelPositionOutsideModelElement } from '@ckeditor/ckeditor5-widget/src/utils';
  import Widget from '@ckeditor/ckeditor5-widget/src/widget';
  import Command from '@ckeditor/ckeditor5-core/src/command';
  
  import { addListToDropdown, createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
  import Collection from '@ckeditor/ckeditor5-utils/src/collection';
  import Model from '@ckeditor/ckeditor5-ui/src/model';
  
  class PlaceholderCommand extends Command {
    execute({ value }) {
      const editor = this.editor;
  
      editor.model.change(writer => {
        // Create <placeholder> elment with name attribute...
        const placeholder = writer.createElement('placeholder', { name: value });
  
        // ... and insert it into the document.
        editor.model.insertContent(placeholder);
  
        // Put the selection on inserted element.
        writer.setSelection(placeholder, 'on');
      });
    }
  
    refresh() {
      const model = this.editor.model;
      const selection = model.document.selection;
  
      const isAllowed = model.schema.checkChild(selection.focus.parent, 'placeholder');
  
      this.isEnabled = isAllowed;
    }
  }
  
  class Placeholder extends Plugin {
    static get requires() {
      return [PlaceholderEditing, PlaceholderUI];
    }
  }
  
  class PlaceholderUI extends Plugin {
    init() {
      const editor = this.editor;
      const t = editor.t;
      const placeholderNames = editor.config.get('placeholderConfig.types');
  
      // The "placeholder" dropdown must be registered among UI components of the editor
      // to be displayed in the toolbar.
      editor.ui.componentFactory.add('placeholder', locale => {
        const dropdownView = createDropdown(locale);
  
        // Populate the list in the dropdown with items.
        addListToDropdown(dropdownView, getDropdownItemsDefinitions(placeholderNames));
  
        dropdownView.buttonView.set({
          // The t() function helps localize the editor. All strings enclosed in t() can be
          // translated and change when the language of the editor changes.
          label: t('Placeholder'),
          tooltip: true,
          withText: true,
        });
  
        // Execute the command when the dropdown items is clicked (executed).
        this.listenTo(dropdownView, 'execute', evt => {
          editor.execute('placeholder', { value: evt.source.commandParam });
          editor.editing.view.focus();
        });
  
        return dropdownView;
      });
    }
  }
  
  function getDropdownItemsDefinitions(placeholderNames) {
    const itemDefinitions = new Collection();
  
    for (const name of placeholderNames) {
      const definition = {
        type: 'button',
        model: new Model({
          commandParam: name,
          label: name,
          withText: true,
        }),
      };
  
      // Add the item definition to the collection.
      itemDefinitions.add(definition);
    }
  
    return itemDefinitions;
  }
  
  class PlaceholderEditing extends Plugin {
    static get requires() {
      return [Widget];
    }
  
    init() {
      console.log('PlaceholderEditing#init() got called');
  
      this._defineSchema();
      this._defineConverters();
  
      this.editor.commands.add('placeholder', new PlaceholderCommand(this.editor));
  
      this.editor.editing.mapper.on(
        'viewToModelPosition',
        viewToModelPositionOutsideModelElement(this.editor.model, viewElement => viewElement.hasClass('placeholder'))
      );
      this.editor.config.define('placeholderConfig', {
        types: ['date', 'first name', 'surname'],
      });
    }
  
    _defineSchema() {
      const schema = this.editor.model.schema;
  
      schema.register('placeholder', {
        // Allow wherever text is allowed:
        allowWhere: '$text',
  
        // The placeholder will acts as an inline node:
        isInline: true,
  
        // The inline-widget is self-contained so cannot be split by the caret and can be selected:
        isObject: true,
  
        // The placeholder can have many types, like date, name, surname, etc:
        allowAttributes: ['name'],
      });
    }
  
    _defineConverters() {
      const conversion = this.editor.conversion;
  
      conversion.for('upcast').elementToElement({
        view: {
          name: 'span',
          classes: ['placeholder'],
        },
        model: (viewElement, modelWriter) => {
          // Extract the "name" from "{name}".
          const name = viewElement.getChild(0).data.slice(1, -1);
  
          return modelWriter.createElement('placeholder', { name });
        },
      });
  
      conversion.for('editingDowncast').elementToElement({
        model: 'placeholder',
        view: (modelItem, viewWriter) => {
          const widgetElement = createPlaceholderView(modelItem, viewWriter);
  
          // Enable widget handling on placeholder element inside editing view.
          return toWidget(widgetElement, viewWriter);
        },
      });
  
      conversion.for('dataDowncast').elementToElement({
        model: 'placeholder',
        view: createPlaceholderView,
      });
  
      // Helper method for both downcast converters.
      function createPlaceholderView(modelItem, viewWriter) {
        const name = modelItem.getAttribute('name');
  
        const placeholderView = viewWriter.createContainerElement('span', {
          class: 'placeholder',
        });
  
        // Insert the placeholder name (as a text).
        const innerText = viewWriter.createText('{' + name + '}');
        viewWriter.insert(viewWriter.createPositionAt(placeholderView, 0), innerText);
  
        return placeholderView;
      }
    }
  }
  
  ClassicEditor.create(document.querySelector('#snippet-inline-widget'), {
    plugins: [Essentials, Paragraph, Heading, List, Bold, Italic, Placeholder],
    toolbar: ['heading', '|', 'bold', 'italic', 'numberedList', 'bulletedList', '|', 'placeholder'],
    placeholderConfig: {
      types: ['date', 'color', 'first name', 'surname'],
    },
  })
    .then(editor => {
      console.log('Editor was initialized', editor);
  
      // Expose for playing in the console.
      window.editor = editor;
    })
    .catch(error => {
      console.error(error.stack);
    });
  