artabro/wire/modules/Inputfield/InputfieldTinyMCE/plugins/pwimage.js
2024-08-27 11:35:37 +02:00

365 lines
No EOL
11 KiB
JavaScript

/**
* pwimage plugin for TinyMCE
*
* @param editor
*
*/
function pwTinyMCE_image(editor) {
var $ = jQuery;
var modalUrl = ProcessWire.config.urls.admin + 'page/image/';
var $link = null; // if img is wrapped in link, this is it
var $figcaption = null; // <figcaption> element when applicable, null otherwise
var $figure = null; // <figure> element when applicable, null otherwise
var selection = editor.selection;
var node = selection.getNode(); // <img> or <body> if inserting new image
var nodeParent = node.parentNode; // <p>, <a> or <figure>
var nodeParentName = nodeParent.nodeName.toUpperCase(); // P, A, or FIGURE
var nodeGrandparent = nodeParent.parentNode; // <figure> or <body> or <p>, <figure> only if nodeParent is <a>
var nodeGrandparentName = nodeGrandparent ? nodeGrandparent.nodeName.toUpperCase() : ''; // FIGURE or BODY or P
var labels = {
captionText: 'Caption text',
savingImage: 'Saving',
insertImage: 'Insert',
selectImage: 'Select',
selectAnotherImage: 'Select another',
cancel: 'Cancel'
};
if(typeof ProcessWire.config.InputfieldTinyMCE.labels !== 'undefined') {
labels = ProcessWire.config.InputfieldTinyMCE.labels; // translated text labels
}
/**
* Insert image
*
* @param src The src attribute for the image to insert
* @param $i iframe contents from ProcessPageEditImageSelect to obtain image settings from
*
*/
function insertImage(src, $i) {
var $img = $('#selected_image', $i);
var width = $img.attr('width');
var alt = $("#selected_image_description", $i).val();
var caption = $("#selected_image_caption", $i).is(":checked");
var $hidpi = $("#selected_image_hidpi", $i);
var hidpi = $hidpi.is(":checked") && !$hidpi.is(":disabled");
var cls = $img
.removeClass('ui-resizable No Alignment resizable_setup')
.removeClass('rotate90 rotate180 rotate270 rotate-90 rotate-180 rotate-270')
.removeClass('flip_vertical flip_horizontal')
.attr('class');
var $linkToLarger = $('#selected_image_link', $i);
var linkToLargerHref = $linkToLarger.is(":checked") ? $linkToLarger.val() : ''; // link to larger version
var $insertElement = $('<img />').attr('src', src).attr('alt', alt);
if(hidpi) $insertElement.addClass('hidpi');
// note: class is added to $figure (rather than <img>) when this is a caption
if(caption === false && cls.length) $insertElement.addClass(cls);
if(width > 0 && $img.attr('data-nosize') != '1') $insertElement.attr('width', width);
if($link) {
// img was wrapped in an <a>...</a> and/or <figure>
if(linkToLargerHref) {
// @todo verify this works and doesn't need similar solution to CKE
// $link.attr('href', link).attr('data-cke-saved-href', link); // populate existing link with new href
} else if($linkToLarger.attr('data-was-checked') == 1) {
// box was checked but no longer is
$link = null;
}
if($link !== null) {
$link.append($insertElement);
$insertElement = $link;
}
} else if(linkToLargerHref) {
$insertElement = $("<a />").attr('href', linkToLargerHref).append($insertElement);
}
if(caption) {
var $figure = $('<figure />');
// $figure.css('width', width + 'px');
if(cls.length) $figure.addClass(cls);
if(!$figcaption) {
$figcaption = $('<figcaption />');
if(alt.length > 1) {
$figcaption.append(alt);
} else {
$figcaption.append(labels.captionText);
}
}
$figure.append($figcaption);
$figure.prepend($insertElement);
$insertElement = $figure;
}
// select the entire element surrounding the image so that we replace it all
if(nodeGrandparentName === 'FIGURE') {
// nodeParent is <a> while nodeGrandparent is <figure>
selection.select(nodeGrandparent);
} else if(nodeParentName === 'A' || nodeParentName === 'FIGURE') {
// @todo check if this works in inline mode
selection.select(nodeParent);
}
var html = $insertElement[0].outerHTML;
// if(figureNodeSafari) figureNodeSafari.remove(); // Safari inserts an extra <figure>, so remove the original
selection.setContent(html);
// editor.insertContent(html);
// editor.fire('change');
}
/**
* Click of the "Insert image" button
*
* @param $iframe The <iframe> element containing ProcessPageEditImageSelect
*
*/
function insertImageButtonClick($iframe) {
var $i = $iframe.contents();
var $img = $('#selected_image', $i);
var width = $img.attr('width');
var height = $img.attr('height');
var file = $img.attr('src');
var imagePageId = $('#page_id', $i).val();
var hidpi = $("#selected_image_hidpi", $i).is(":checked") ? 1 : 0;
var rotate = parseInt($("#selected_image_rotate", $i).val());
$iframe.dialog('disable');
$iframe.setTitle(labels.savingImage); // Saving Image
$img.removeClass("resized");
if(!width) width = $img.width();
if(!height) height = $img.height();
file = file.substring(file.lastIndexOf('/')+1);
var resizeUrl = modalUrl + 'resize' +
'?id=' + imagePageId +
'&file=' + file +
'&width=' + width +
'&height=' + height +
'&hidpi=' + hidpi;
if(rotate) resizeUrl += '&rotate=' + rotate;
if($img.hasClass('flip_horizontal')) {
resizeUrl += '&flip=h';
} else if($img.hasClass('flip_vertical')) {
resizeUrl += '&flip=v';
}
$.get(resizeUrl, function(data) {
var $div = $("<div></div>").html(data);
var src = $div.find('#selected_image').attr('src');
insertImage(src, $i);
$iframe.dialog('close');
});
}
/**
* Image editor iframe load event (ProcessPageEditImageSelect)
*
* @param $iframe
*
*/
function iframeLoad($iframe) {
// when iframe loads, pull the contents into $i
var $i = $iframe.contents();
var $img = $('#selected_image', $i);
var buttons = [];
if($img.length > 0) {
buttons = [ {
// Insert image button
html: "<i class='fa fa-camera'></i> " + labels.insertImage,
click: function() {
insertImageButtonClick($iframe);
}
}, {
// Select another image button
html: "<i class='fa fa-folder-open'></i> " + labels.selectAnotherImage, // "Select Another Image",
class: 'ui-priority-secondary',
click: function() {
var $i = $iframe.contents();
var imagePageId = $('#page_id', $i).val();
$iframe.attr('src', modalUrl + '?id=' + imagePageId + '&modal=1');
$iframe.setButtons({});
}
} ];
} else {
// no #selected_image element on page
// duplicate buttons in iframe and add a cancel button
$('button.pw-modal-button, button[type=submit]:visible', $i).each(function() {
var $button = $(this);
var button = {
html: $button.html(),
click: function() {
$button.trigger('click');
}
}
buttons.push(button);
if(!$button.hasClass('pw-modal-button-visible')) $button.hide();
});
}
buttons.push({
// cancel button
html: "<i class='fa fa-times-circle'></i> " + labels.cancel, // "Cancel",
class: 'ui-priority-secondary',
click: function() { $iframe.dialog('close'); }
});
$iframe.setButtons(buttons);
$iframe.setTitle($i.find('title').html());
}
/**
* build query string for initial load of ProcessPageEditImageSelect
*
* @returns {string}
*
*/
function buildQueryString() {
var imgWidth, imgHeight, imgDescription, imgLink, imgClass, nodeClass, hidpi, file = '';
var $editor = $('#' + editor.id);
var $inputfield = $editor.closest('.Inputfield');
var $node = $(node);
var $in = $("#Inputfield_id");
var pageId = $in.length ? $in.val() : $inputfield.attr('data-pid');
var editPageId = pageId;
var src = $node.attr('src');
var queryString;
var $repeaterItem = $inputfield.closest('.InputfieldRepeaterItem');
if(src) {
imgClass = $node.attr('class'); // class for img only
nodeClass = $figure ? $figure.attr('class') : $node.attr('class'); // class for img OR figure
hidpi = imgClass && imgClass.indexOf('hidpi') > -1;
imgWidth = $node.attr('width');
imgHeight = $node.attr('height');
imgDescription = $node.attr('alt');
imgLink = nodeParentName === 'A' ? $(nodeParent).attr('href') : '';
var parts = src.split('/');
file = parts.pop();
parts = parts.reverse();
var pathPageId = '';
// pull page_id out of img[src]
for(var n = 0; n < parts.length; n++) {
// accounts for either /1/2/3/ or /123/ format
if(parts[n].match(/^\d+$/)) {
pathPageId = parts[n] + pathPageId;
} else if(pathPageId.length) {
break;
}
}
if(pathPageId.length) pageId = parseInt(pathPageId);
}
if($repeaterItem.length && $repeaterItem.find('.InputfieldImage').length) {
var dataPageAttr = $repeaterItem.attr('data-page');
if(typeof dataPageAttr !== 'undefined') pageId = parseInt(dataPageAttr);
}
queryString = '?id=' + pageId + '&edit_page_id=' + editPageId + '&modal=1';
if(file.length) queryString += "&file=" + file;
if(imgWidth) queryString += "&width=" + imgWidth;
if(imgHeight) queryString += "&height=" + imgHeight;
if(nodeClass && nodeClass.length) queryString += "&class=" + encodeURIComponent(nodeClass);
queryString += '&hidpi=' + (hidpi ? '1' : '0');
if(imgDescription && imgDescription.length) {
queryString += "&description=" + encodeURIComponent(imgDescription);
}
if($figcaption) queryString += "&caption=1";
if(imgLink && imgLink.length) queryString += "&link=" + encodeURIComponent(imgLink);
queryString += ('&winwidth=' + ($(window).width() - 30));
return queryString;
}
/**
* Initialize
*
*/
function init() {
// identify if we are in a <figure>
if(nodeGrandparentName === 'FIGURE') {
$figure = $(nodeGrandparent.outerHTML);
} else if(nodeParentName === 'FIGURE') {
$figure = $(nodeParent.outerHTML);
}
if($figure) {
$figcaption = $figure.find('figcaption');
$figure.find('img').remove();
}
// identify if we are in an <a>
if(nodeParentName === 'A') {
$link = $(nodeParent.outerHTML);
$link.find('img').remove();
}
// create iframe dialog box
var modalSettings = {
title: "<i class='fa fa-fw fa-folder-open'></i> " + labels.selectImage, // "Select Image",
open: function() {
// if(jQuery(".cke_maximized").length > 0) {
// the following is required when CKE is maximized to make sure dialog is on top of it
// jQuery('.ui-dialog').css('z-index', 9999);
// jQuery('.ui-widget-overlay').css('z-index', 9998);
// }
}
};
var $iframe = pwModalWindow(modalUrl + buildQueryString(), modalSettings, 'large');
$iframe.on('load', function() { iframeLoad($iframe); });
}
init();
}
/**
* Add pwimage to TinyMCE plugin manager
*
*/
tinymce.PluginManager.add('pwimage', (editor, url) => {
editor.ui.registry.addButton('pwimage', {
text: '',
icon: 'image',
onAction: function() {
pwTinyMCE_image(editor);
}
});
// Adds a menu item, which can then be included in any menu via the menu/menubar configuration
editor.ui.registry.addMenuItem('pwimage', {
text: 'Image',
icon: 'image',
onAction: function() {
pwTinyMCE_image(editor);
}
});
// context menu
editor.ui.registry.addContextMenu('pwimage', {
update: (element) => !element.src ? '' : 'pwimage'
});
// double click on image loads image editor
editor.on('dblclick', function(e) {
if(e.target.nodeName === 'IMG') {
pwTinyMCE_image(editor);
}
});
// Return metadata for the plugin
return {
getMetadata: () => ({ name: 'Image' })
};
});