mirror of
https://github.com/codex-team/editor.js
synced 2024-06-13 03:12:21 +02:00
701 lines
19 KiB
JavaScript
701 lines
19 KiB
JavaScript
/**
|
||
* Image plugin for codex-editor
|
||
* @author CodeX Team <team@ifmo.su>
|
||
*
|
||
* @version 1.3.0
|
||
*/
|
||
var image = (function(image_plugin) {
|
||
|
||
/**
|
||
* @private
|
||
*
|
||
* CSS classNames
|
||
*/
|
||
var elementClasses_ = {
|
||
|
||
ce_image : 'ce-image',
|
||
loading : 'ce-plugin-image__loader',
|
||
blockStretched: 'ce-block--stretched',
|
||
uploadedImage : {
|
||
centered : 'ce-plugin-image__uploaded--centered',
|
||
stretched : 'ce-plugin-image__uploaded--stretched'
|
||
},
|
||
imageCaption : 'ce-plugin-image__caption',
|
||
imageWrapper : 'ce-plugin-image__wrapper',
|
||
formHolder : 'ce-plugin-image__holder',
|
||
uploadButton : 'ce-plugin-image__button',
|
||
imagePreview : 'ce-image__preview',
|
||
selectorHolder: 'ce-settings-checkbox',
|
||
selectorButton: 'ce-settings-checkbox__toggler',
|
||
settingsItem: 'ce-image-settings__item',
|
||
imageWrapperBordered : 'ce-image__wrapper--bordered',
|
||
toggled : 'ce-image-settings__item--toggled'
|
||
|
||
};
|
||
|
||
/**
|
||
*
|
||
* @private
|
||
*
|
||
* UI methods
|
||
*/
|
||
var ui_ = {
|
||
|
||
holder : function(){
|
||
|
||
var element = document.createElement('DIV');
|
||
|
||
element.classList.add(elementClasses_.formHolder);
|
||
element.classList.add(elementClasses_.ce_image);
|
||
|
||
return element;
|
||
},
|
||
|
||
uploadButton : function(){
|
||
|
||
var button = document.createElement('SPAN');
|
||
|
||
button.classList.add(elementClasses_.uploadButton);
|
||
|
||
button.innerHTML = '<i class="ce-icon-picture"> </i>';
|
||
button.innerHTML += 'Загрузить фотографию';
|
||
|
||
return button;
|
||
|
||
},
|
||
|
||
/**
|
||
* @param {object} file - file path
|
||
* @param {string} style - css class
|
||
* @return {object} image - document IMG tag
|
||
*/
|
||
image : function(file, styles) {
|
||
|
||
var image = document.createElement('IMG');
|
||
|
||
styles.map(function(item) {
|
||
image.classList.add(item);
|
||
});
|
||
|
||
image.src = file.url;
|
||
image.dataset.bigUrl = file.bigUrl;
|
||
|
||
return image;
|
||
},
|
||
|
||
wrapper : function() {
|
||
|
||
var div = document.createElement('div');
|
||
|
||
div.classList.add(elementClasses_.imageWrapper);
|
||
|
||
return div;
|
||
},
|
||
|
||
caption : function() {
|
||
|
||
var div = document.createElement('div');
|
||
|
||
div.classList.add(elementClasses_.imageCaption);
|
||
div.contentEditable = true;
|
||
|
||
return div;
|
||
},
|
||
/**
|
||
* Draws form for image upload
|
||
*/
|
||
makeForm : function() {
|
||
|
||
var holder = ui_.holder(),
|
||
uploadButton = ui_.uploadButton();
|
||
|
||
holder.appendChild(uploadButton);
|
||
|
||
uploadButton.addEventListener('click', uploadButtonClicked_, false );
|
||
|
||
image.holder = holder;
|
||
|
||
return holder;
|
||
},
|
||
|
||
|
||
/**
|
||
* wraps image and caption
|
||
* @param {object} data - image information
|
||
* @param {string} imageTypeClass - plugin's style
|
||
* @param {boolean} stretched - stretched or not
|
||
* @return wrapped block with image and caption
|
||
*/
|
||
makeImage : function(data, imageTypeClasses, stretched, bordered) {
|
||
|
||
var file = data,
|
||
text = data.caption,
|
||
type = data.type,
|
||
image = ui_.image(file, imageTypeClasses),
|
||
caption = ui_.caption(),
|
||
wrapper = ui_.wrapper();
|
||
|
||
caption.innerHTML = text || '';
|
||
|
||
wrapper.dataset.stretched = stretched;
|
||
wrapper.dataset.bordered = bordered;
|
||
|
||
/** Appeding to the wrapper */
|
||
wrapper.appendChild(image);
|
||
wrapper.appendChild(caption);
|
||
|
||
return wrapper;
|
||
},
|
||
|
||
/**
|
||
* @param {HTML} data - Rendered block with image
|
||
*/
|
||
getImage : function(data) {
|
||
|
||
var image = data.querySelector('.' + elementClasses_.uploadedImage.centered) ||
|
||
data.querySelector('.' + elementClasses_.uploadedImage.stretched);
|
||
|
||
return image;
|
||
},
|
||
|
||
/**
|
||
* wraps image and caption
|
||
* @deprecated
|
||
* @param {object} data - image information
|
||
* @return wrapped block with image and caption
|
||
*/
|
||
centeredImage : function(data) {
|
||
|
||
var file = data.file,
|
||
text = data.caption,
|
||
type = data.type,
|
||
image = ui_.image(file, elementClasses_.uploadedImage.centered),
|
||
caption = ui_.caption(),
|
||
wrapper = ui_.wrapper();
|
||
|
||
caption.textContent = text;
|
||
|
||
wrapper.dataset.stretched = 'false';
|
||
|
||
/** Appeding to the wrapper */
|
||
wrapper.appendChild(image);
|
||
wrapper.appendChild(caption);
|
||
|
||
return wrapper;
|
||
},
|
||
|
||
/**
|
||
* wraps image and caption
|
||
* @deprecated
|
||
* @param {object} data - image information
|
||
* @return stretched image
|
||
*/
|
||
stretchedImage : function(data) {
|
||
|
||
var file = data.file,
|
||
text = data.caption,
|
||
type = data.type,
|
||
image = ui_.image(file, elementClasses_.uploadedImage.stretched),
|
||
caption = ui_.caption(),
|
||
wrapper = ui_.wrapper();
|
||
|
||
caption.textContent = text;
|
||
|
||
wrapper.dataset.stretched = 'true';
|
||
|
||
/** Appeding to the wrapper */
|
||
wrapper.appendChild(image);
|
||
wrapper.appendChild(caption);
|
||
|
||
return wrapper;
|
||
|
||
}
|
||
|
||
};
|
||
|
||
/**
|
||
* @private
|
||
*
|
||
* After render callback
|
||
*/
|
||
var uploadButtonClicked_ = function(event) {
|
||
|
||
var url = image_plugin.config.uploadImage,
|
||
beforeSend = uploadingCallbacks_.ByClick.beforeSend,
|
||
success = uploadingCallbacks_.ByClick.success,
|
||
error = uploadingCallbacks_.ByClick.error;
|
||
|
||
/** Define callbacks */
|
||
codex.editor.transport.selectAndUpload({
|
||
url : url,
|
||
multiple : false,
|
||
accept : 'image/*',
|
||
beforeSend : beforeSend,
|
||
success : success,
|
||
error : error
|
||
});
|
||
};
|
||
|
||
var methods_ = {
|
||
|
||
addSelectTypeClickListener : function(el, type) {
|
||
|
||
el.addEventListener('click', function() {
|
||
|
||
// el - settings element
|
||
|
||
switch (type) {
|
||
case 'bordered':
|
||
methods_.toggleBordered(type, this); break;
|
||
case 'stretched':
|
||
methods_.toggleStretched(type, this); break;
|
||
}
|
||
|
||
|
||
}, false);
|
||
|
||
},
|
||
|
||
toggleBordered : function(type, clickedSettingsItem ) {
|
||
|
||
var current = codex.editor.content.currentNode,
|
||
blockContent = current.childNodes[0],
|
||
img = ui_.getImage(current),
|
||
wrapper = current.querySelector('.' + elementClasses_.imageWrapper);
|
||
|
||
if (!img) {
|
||
return;
|
||
}
|
||
|
||
/**
|
||
* Add classes to the IMG tag and to the Settings element
|
||
*/
|
||
img.classList.toggle(elementClasses_.imageWrapperBordered);
|
||
clickedSettingsItem.classList.toggle(elementClasses_.toggled);
|
||
|
||
/**
|
||
* Save settings in dataset
|
||
*/
|
||
wrapper.dataset.bordered = img.classList.contains(elementClasses_.imageWrapperBordered);
|
||
|
||
setTimeout(function() {
|
||
codex.editor.toolbar.settings.close();
|
||
}, 200);
|
||
|
||
},
|
||
|
||
toggleStretched : function( type, clickedSettingsItem ) {
|
||
|
||
var current = codex.editor.content.currentNode,
|
||
blockContent = current.childNodes[0],
|
||
img = ui_.getImage(current),
|
||
wrapper = current.querySelector('.' + elementClasses_.imageWrapper);
|
||
|
||
if (!img) {
|
||
return;
|
||
}
|
||
|
||
/** Clear classList */
|
||
blockContent.classList.add(elementClasses_.blockStretched);
|
||
img.classList.toggle(elementClasses_.uploadedImage.stretched);
|
||
img.classList.toggle(elementClasses_.uploadedImage.centered);
|
||
|
||
clickedSettingsItem.classList.toggle(elementClasses_.toggled);
|
||
|
||
wrapper.dataset.stretched = img.classList.contains(elementClasses_.uploadedImage.stretched);
|
||
|
||
setTimeout(function() {
|
||
codex.editor.toolbar.settings.close();
|
||
}, 1000);
|
||
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @private
|
||
* Callbacks
|
||
*/
|
||
var uploadingCallbacks_ = {
|
||
|
||
ByClick : {
|
||
|
||
/**
|
||
* Before sending ajax request
|
||
*/
|
||
beforeSend : function() {
|
||
|
||
var input = codex.editor.transport.input,
|
||
files = input.files;
|
||
|
||
var validFileExtensions = ["jpg", "jpeg", "bmp", "gif", "png"];
|
||
|
||
var type = files[0].type.split('/');
|
||
|
||
var result = validFileExtensions.some(function(ext) {
|
||
return ext == type[1];
|
||
});
|
||
|
||
if (!result) {
|
||
return;
|
||
}
|
||
|
||
var reader = new FileReader();
|
||
reader.readAsDataURL(files[0]);
|
||
|
||
reader.onload = function(e) {
|
||
|
||
var data = {
|
||
background : false,
|
||
border : false,
|
||
isstretch : false,
|
||
url : e.target.result,
|
||
bigUrl : null,
|
||
width : null,
|
||
height : null,
|
||
additionalData : null,
|
||
caption : '',
|
||
cover : null
|
||
};
|
||
|
||
var newImage = make_(data);
|
||
|
||
codex.editor.content.switchBlock(image.holder, newImage, 'image');
|
||
newImage.classList.add(elementClasses_.imagePreview);
|
||
|
||
/**
|
||
* Change holder to image
|
||
*/
|
||
image.holder = newImage;
|
||
};
|
||
|
||
},
|
||
|
||
/** Photo was uploaded successfully */
|
||
success : function(result) {
|
||
|
||
var parsed = JSON.parse(result),
|
||
data,
|
||
currentBlock = codex.editor.content.currentNode;
|
||
|
||
/**
|
||
* Preparing {Object} data to draw an image
|
||
* @uses ceImage.make method
|
||
*/
|
||
data = parsed.data;
|
||
|
||
image.holder.classList.remove(elementClasses_.imagePreview);
|
||
|
||
/**
|
||
* Change src of image
|
||
*/
|
||
var newImage = image.holder.getElementsByTagName('IMG')[0];
|
||
|
||
newImage.src = parsed.data.url;
|
||
newImage.dataset.bigUrl = parsed.data.bigUrl;
|
||
newImage.dataset.width = parsed.data.width;
|
||
newImage.dataset.height = parsed.data.height;
|
||
newImage.dataset.additionalData = parsed.data.additionalData;
|
||
|
||
},
|
||
|
||
/** Error callback. Sends notification to user that something happend or plugin doesn't supports method */
|
||
error : function(result) {
|
||
|
||
var oldHolder = image.holder;
|
||
var form = ui_.makeForm();
|
||
|
||
codex.editor.content.switchBlock(oldHolder, form, 'image');
|
||
|
||
}
|
||
},
|
||
|
||
ByPaste : {
|
||
|
||
/**
|
||
* Direct upload
|
||
* Any URL that contains image extension
|
||
* @param url
|
||
*/
|
||
uploadImageFromUrl : function(path) {
|
||
|
||
var image,
|
||
current = codex.editor.content.currentNode,
|
||
beforeSend,
|
||
success_callback;
|
||
|
||
/** When image is uploaded to redactors folder */
|
||
success_callback = function(data) {
|
||
|
||
var imageInfo = JSON.parse(data);
|
||
|
||
var newImage = image.getElementsByTagName('IMG')[0];
|
||
|
||
newImage.dataset.stretched = false;
|
||
newImage.dataset.src = imageInfo.url;
|
||
newImage.dataset.bigUrl = imageInfo.bigUrl;
|
||
newImage.dataset.width = imageInfo.width;
|
||
newImage.dataset.height = imageInfo.height;
|
||
newImage.dataset.additionalData = imageInfo.additionalData;
|
||
|
||
image.classList.remove(elementClasses_.imagePreview);
|
||
|
||
};
|
||
|
||
/** Before sending XMLHTTP request */
|
||
beforeSend = function() {
|
||
|
||
var content = current.querySelector('.ce-block__content');
|
||
|
||
var data = {
|
||
background: false,
|
||
border: false,
|
||
isStretch: false,
|
||
file: {
|
||
url: path,
|
||
bigUrl: null,
|
||
width: null,
|
||
height: null,
|
||
additionalData: null
|
||
},
|
||
caption: '',
|
||
cover: null
|
||
};
|
||
|
||
image = codex.editor.tools.image_extended.render(data);
|
||
|
||
image.classList.add(elementClasses_.imagePreview);
|
||
|
||
var img = image.querySelector('img');
|
||
|
||
codex.editor.content.switchBlock(codex.editor.content.currentNode, image, 'image');
|
||
|
||
};
|
||
|
||
/** Preparing data for XMLHTTP */
|
||
var data = {
|
||
url: image_plugin.config.uploadFromUrl,
|
||
type: "POST",
|
||
data : {
|
||
url: path
|
||
},
|
||
beforeSend : beforeSend,
|
||
success : success_callback
|
||
};
|
||
|
||
codex.editor.core.ajax(data);
|
||
}
|
||
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Image path
|
||
* @type {null}
|
||
*/
|
||
image_plugin.path = null;
|
||
|
||
/**
|
||
* Plugin configuration
|
||
*/
|
||
image_plugin.config = null;
|
||
|
||
/**
|
||
*
|
||
* @private
|
||
*
|
||
* @param data
|
||
* @return {*}
|
||
*
|
||
*/
|
||
var make_ = function ( data ) {
|
||
|
||
var holder,
|
||
classes = [];
|
||
|
||
if (data) {
|
||
|
||
if (data.border) {
|
||
classes.push(elementClasses_.imageWrapperBordered);
|
||
}
|
||
|
||
if ( data.isstretch || data.isstretch === 'true') {
|
||
|
||
classes.push(elementClasses_.uploadedImage.stretched);
|
||
holder = ui_.makeImage(data, classes, 'true', data.border);
|
||
|
||
} else {
|
||
|
||
classes.push(elementClasses_.uploadedImage.centered);
|
||
holder = ui_.makeImage(data, classes, 'false', data.border);
|
||
|
||
}
|
||
|
||
return holder;
|
||
|
||
} else {
|
||
|
||
holder = ui_.makeForm();
|
||
|
||
return holder;
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @private
|
||
*
|
||
* Prepare clear data before save
|
||
*
|
||
* @param data
|
||
*/
|
||
var prepareDataForSave_ = function(data) {
|
||
|
||
};
|
||
|
||
/**
|
||
* @public
|
||
* @param config
|
||
*/
|
||
image_plugin.prepare = function(config) {
|
||
|
||
image_plugin.config = config;
|
||
|
||
return Promise.resolve();
|
||
};
|
||
|
||
/**
|
||
* @public
|
||
*
|
||
* this tool works when tool is clicked in toolbox
|
||
*/
|
||
image_plugin.appendCallback = function(event) {
|
||
|
||
/** Upload image and call success callback*/
|
||
uploadButtonClicked_(event);
|
||
|
||
};
|
||
|
||
/**
|
||
* @public
|
||
*
|
||
* @param data
|
||
* @return {*}
|
||
*/
|
||
image_plugin.render = function( data ) {
|
||
|
||
return make_(data);
|
||
};
|
||
|
||
/**
|
||
* @public
|
||
*
|
||
* @param block
|
||
* @return {{background: boolean, border: boolean, isstretch: boolean, file: {url: (*|string|Object), bigUrl: (null|*), width: *, height: *, additionalData: null}, caption: (string|*|string), cover: null}}
|
||
*/
|
||
image_plugin.save = function ( block ) {
|
||
|
||
var content = block,
|
||
image = ui_.getImage(content),
|
||
caption = content.querySelector('.' + elementClasses_.imageCaption);
|
||
|
||
var data = {
|
||
background : false,
|
||
border : content.dataset.bordered === 'true' ? true : false,
|
||
isstretch : content.dataset.stretched === 'true' ? true : false,
|
||
// file : {
|
||
url : image.dataset.src || image.src,
|
||
bigUrl : image.dataset.bigUrl,
|
||
width : image.width,
|
||
height : image.height,
|
||
additionalData :null,
|
||
// },
|
||
caption : caption.innerHTML || '',
|
||
cover : null
|
||
};
|
||
|
||
return data;
|
||
};
|
||
|
||
/**
|
||
* @public
|
||
*
|
||
* Settings panel content
|
||
* @return {Element} element contains all settings
|
||
*/
|
||
image_plugin.makeSettings = function () {
|
||
|
||
var currentNode = codex.editor.content.currentNode,
|
||
wrapper = currentNode.querySelector('.' + elementClasses_.imageWrapper),
|
||
holder = document.createElement('DIV'),
|
||
types = {
|
||
stretched : "На всю ширину",
|
||
bordered : "Добавить рамку"
|
||
},
|
||
currentImageWrapper = currentNode.querySelector('.' + elementClasses_.imageWrapper ),
|
||
currentImageSettings = currentImageWrapper.dataset;
|
||
|
||
/** Add holder classname */
|
||
holder.className = 'ce-image-settings';
|
||
|
||
/** Now add type selectors */
|
||
for (var type in types){
|
||
|
||
/**
|
||
* Settings template
|
||
*/
|
||
var settingsItem = document.createElement('DIV'),
|
||
selectorsHolder = document.createElement('SPAN'),
|
||
selectorsButton = document.createElement('SPAN');
|
||
|
||
settingsItem.classList.add(elementClasses_.settingsItem);
|
||
selectorsHolder.classList.add(elementClasses_.selectorHolder);
|
||
selectorsButton.classList.add(elementClasses_.selectorButton);
|
||
|
||
selectorsHolder.appendChild(selectorsButton);
|
||
settingsItem.appendChild(selectorsHolder);
|
||
|
||
selectTypeButton = document.createTextNode(types[type]);
|
||
settingsItem.appendChild(selectTypeButton);
|
||
|
||
/**
|
||
* Activate previously selected settings
|
||
*/
|
||
if ( currentImageSettings[type] == 'true' ){
|
||
settingsItem.classList.add(elementClasses_.toggled);
|
||
}
|
||
|
||
methods_.addSelectTypeClickListener(settingsItem, type);
|
||
|
||
holder.appendChild(settingsItem);
|
||
|
||
}
|
||
|
||
return holder;
|
||
|
||
};
|
||
|
||
/**
|
||
* Share as API
|
||
*/
|
||
image_plugin.uploadImageFromUri = uploadingCallbacks_.ByPaste.uploadImageFromUrl;
|
||
|
||
image_plugin.pastePatterns = [
|
||
{
|
||
type: 'image',
|
||
regex: /(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*\.(?:jpe?g|gif|png))(?:\?([^#]*))?(?:#(.*))?/i,
|
||
callback: image_plugin.uploadImageFromUri
|
||
},
|
||
{
|
||
type: 'uploadCare',
|
||
regex: /^https:\/\/(uploadcare\.cmtt\.ru|ucarecdn\.com|static[0-9]+\.siliconrus\.cmtt\.ru|static[0-9]+\.cmtt\.ru)/i,
|
||
callback: image_plugin.uploadImageFromUri
|
||
} ];
|
||
|
||
image_plugin.destroy = function () {
|
||
|
||
image = null;
|
||
|
||
};
|
||
|
||
return image_plugin;
|
||
|
||
})({}); |