ckeditor5-image-upload-base64
Version:
The development environment of CKEditor 5 – the best browser-based rich text editor.
170 lines (139 loc) • 5.52 kB
JavaScript
/**
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module image/image/utils
*/
import { findOptimalInsertionPosition, isWidget, toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
/**
* Converts a given {@link module:engine/view/element~Element} to an image widget:
* * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to recognize the image widget element.
* * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
*
* @param {module:engine/view/element~Element} viewElement
* @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
* @param {String} label The element's label. It will be concatenated with the image `alt` attribute if one is present.
* @returns {module:engine/view/element~Element}
*/
export function toImageWidget( viewElement, writer, label ) {
writer.setCustomProperty( 'image', true, viewElement );
return toWidget( viewElement, writer, { label: labelCreator } );
function labelCreator() {
const imgElement = getViewImgFromWidget( viewElement );
const altText = imgElement.getAttribute( 'alt' );
return altText ? `${ altText } ${ label }` : label;
}
}
/**
* Checks if a given view element is an image widget.
*
* @param {module:engine/view/element~Element} viewElement
* @returns {Boolean}
*/
export function isImageWidget( viewElement ) {
return !!viewElement.getCustomProperty( 'image' ) && isWidget( viewElement );
}
/**
* Returns an image widget editing view element if one is selected.
*
* @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} selection
* @returns {module:engine/view/element~Element|null}
*/
export function getSelectedImageWidget( selection ) {
const viewElement = selection.getSelectedElement();
if ( viewElement && isImageWidget( viewElement ) ) {
return viewElement;
}
return null;
}
/**
* Checks if the provided model element is an `image`.
*
* @param {module:engine/model/element~Element} modelElement
* @returns {Boolean}
*/
export function isImage( modelElement ) {
return !!modelElement && modelElement.is( 'element', 'image' );
}
/**
* Handles inserting single file. This method unifies image insertion using {@link module:widget/utils~findOptimalInsertionPosition} method.
*
* model.change( writer => {
* insertImage( writer, model, { src: 'path/to/image.jpg' } );
* } );
*
* @param {module:engine/model/writer~Writer} writer
* @param {module:engine/model/model~Model} model
* @param {Object} [attributes={}] Attributes of inserted image
*/
export function insertImage( writer, model, attributes = {} ) {
const imageElement = writer.createElement( 'image', attributes );
const insertAtSelection = findOptimalInsertionPosition( model.document.selection, model );
model.insertContent( imageElement, insertAtSelection );
// Inserting an image might've failed due to schema regulations.
if ( imageElement.parent ) {
writer.setSelection( imageElement, 'on' );
}
}
/**
* Checks if image can be inserted at current model selection.
*
* @param {module:engine/model/model~Model} model
* @returns {Boolean}
*/
export function isImageAllowed( model ) {
const schema = model.schema;
const selection = model.document.selection;
return isImageAllowedInParent( selection, schema, model ) &&
!checkSelectionOnObject( selection, schema ) &&
isInOtherImage( selection );
}
/**
* Get view `<img>` element from the view widget (`<figure>`).
*
* Assuming that image is always a first child of a widget (ie. `figureView.getChild( 0 )`) is unsafe as other features might
* inject their own elements to the widget.
*
* The `<img>` can be wrapped to other elements, e.g. `<a>`. Nested check required.
*
* @param {module:engine/view/element~Element} figureView
* @returns {module:engine/view/element~Element}
*/
export function getViewImgFromWidget( figureView ) {
const figureChildren = [];
for ( const figureChild of figureView.getChildren() ) {
figureChildren.push( figureChild );
if ( figureChild.is( 'element' ) ) {
figureChildren.push( ...figureChild.getChildren() );
}
}
return figureChildren.find( viewChild => viewChild.is( 'element', 'img' ) );
}
// Checks if image is allowed by schema in optimal insertion parent.
//
// @returns {Boolean}
function isImageAllowedInParent( selection, schema, model ) {
const parent = getInsertImageParent( selection, model );
return schema.checkChild( parent, 'image' );
}
// Check if selection is on object.
//
// @returns {Boolean}
function checkSelectionOnObject( selection, schema ) {
const selectedElement = selection.getSelectedElement();
return selectedElement && schema.isObject( selectedElement );
}
// Checks if selection is placed in other image (ie. in caption).
function isInOtherImage( selection ) {
return [ ...selection.focus.getAncestors() ].every( ancestor => !ancestor.is( 'element', 'image' ) );
}
// Returns a node that will be used to insert image with `model.insertContent` to check if image can be placed there.
function getInsertImageParent( selection, model ) {
const insertAt = findOptimalInsertionPosition( selection, model );
const parent = insertAt.parent;
if ( parent.isEmpty && !parent.is( 'element', '$root' ) ) {
return parent.parent;
}
return parent;
}