visualn-8.x-1.x-dev/modules/visualn_embed/js/plugins/drupalvisualndrawing/plugin.js
modules/visualn_embed/js/plugins/drupalvisualndrawing/plugin.js
(function ($, Drupal, drupalSettings, CKEDITOR) {
"use strict";
CKEDITOR.plugins.add('drupalvisualndrawing', {
// This plugin requires the Widgets System defined in the 'widget' plugin.
requires: 'widget',
icons: 'imagepopup',
hidpi: true,
beforeInit: function (editor) {
// Configure CKEditor DTD for custom drupal-entity element.
// @see https://www.drupal.org/node/2448449#comment-9717735
var dtd = CKEDITOR.dtd, tagName;
dtd['drupal-visualn-drawing'] = {'#': 1};
// Register drupal-visualn-drawing element as allowed child, in each tag that can
// contain a div element.
for (tagName in dtd) {
if (dtd[tagName].div) {
dtd[tagName]['drupal-visualn-drawing'] = 1;
}
}
// Implementation before initializing plugin.
editor.addCommand( 'editdrawing', {
// @todo: is 'align' and other properties required here?
allowedContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
//allowedContent: 'drupal-visualn-drawing[data-visualn-drawing-id,data-visualn-drawing-align]',
requiredContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
modes: { wysiwyg : 1 },
canUndo: true,
exec: function (editor, data) {
var myPluginSaveCallback = function(data) {
//console.log(data.drawing_id);
var entityElement = editor.document.createElement('drupal-visualn-drawing');
var attributes = data.tag_attributes;
for (var key in attributes) {
entityElement.setAttribute(key, attributes[key]);
}
editor.insertHtml(entityElement.getOuterHtml());
};
var drawing_id = getSelectionDrawingId(editor);
// @todo: use existingElement if keeping drawing properties after replacement is neeeded
// though iframe properties should be reset/updated then since iframe entry
// would point to a different drawing (reset hash or keep hash and update entry)
//var existingElement = getSelectedEmbeddedDrawing(editor);
// Populate existing values of form.
var existingValues = {};
if (drawing_id) {
existingValues['data-visualn-drawing-id'] = drawing_id;
}
var dialogSettings = {
dialogClass: 'ui-dialog-visualn'
};
// Open dialog form.
Drupal.ckeditor.openDialog(editor,
Drupal.url('visualn_embed/form/drawing_embed_dialog/' + editor.config.drupal.format),
existingValues,
myPluginSaveCallback,
dialogSettings
);
}
});
// @todo: rework all these methods and the file code itself completely
editor.addCommand( 'editvisualndrawing', {
allowedContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
requiredContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
modes: { wysiwyg : 1 },
canUndo: false,
//canUndo: true,
exec: function (editor, data) {
// @todo: this is a copy-paste form editdrawing method
var myPluginSaveCallback = function(data) {
var entityElement = editor.document.createElement('drupal-visualn-drawing');
var attributes = data.tag_attributes;
for (var key in attributes) {
entityElement.setAttribute(key, attributes[key]);
}
editor.insertHtml(entityElement.getOuterHtml());
};
var drawing_id = getSelectionDrawingId(editor);
// @todo: check drawing_id
//var drawing_id = existingElement.data('visualn-drawing-id');
//var drawing_id = attribute.nodeValue;
// @todo: add callback that would change ckeditor drawing markup (e.g. at title update)
// @todo: the route callback should check if drawings exists
// @todo: callback paths could be passed as settings from server-side (or should be tracked for changes)
var existingValues = {};
// @todo: also see https://www.drupal.org/project/drupal/issues/1918744
var dialogSettings = {
// @todo: this is getting overridden in ckeditor.js
// see Drupal.ckeditor.openDialog()
//width: '800'
dialogClass: 'ui-dialog-visualn'
};
// @todo: check permissions if user has access to edit the drawing (same for preview and other actions)
// also do not show inactive menu items for 'Drawing not found' case
// Open dialog form.
// @todo: how to pass dialog id so that it could be close on save by ajax commands response?
Drupal.ckeditor.openDialog(editor,
//Drupal.url('visualn_embed/form/drawing_embed_dialog/' + editor.config.drupal.format),
Drupal.url('visualn-ckeditor/drawing/' + drawing_id + '/edit_content'),
//Drupal.url('visualn-ckeditor/drawing/' + drawing_id + '/edit'),
//Drupal.url('my_module/dialog/image/' + editor.config.drupal.format),
existingValues,
myPluginSaveCallback,
dialogSettings
);
return;
}
});
editor.addCommand( 'previewvisualndrawing', {
allowedContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
requiredContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
modes: { wysiwyg : 1 },
canUndo: false,
//canUndo: true,
exec: function (editor, data) {
var myPluginSaveCallback = function(data) {
//console.log(data.drawing_id);
// @todo: what if user clicked 'edit' on the preview and edited the drawing
//console.log('preview content');
};
var drawing_id = getSelectionDrawingId(editor);
// Populate existing values of form.
var existingValues = {};
var dialogSettings = {
dialogClass: 'ui-dialog-visualn'
};
// Open dialog form.
Drupal.ckeditor.openDialog(editor,
Drupal.url('visualn-drawing-embed/real-preview-content/' + drawing_id),
existingValues,
myPluginSaveCallback,
dialogSettings
);
}
});
editor.addCommand( 'replacevisualndrawing', {
allowedContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
requiredContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
modes: { wysiwyg : 1 },
canUndo: false,
//canUndo: true,
exec: function (editor, data) {
// @todo: consider an option to keep drawing properties for replacing drawing
// @todo: doesn't undo
// @todo: this is mostly a copy-paste form insert command (currently called editvisualndrawing)
// @todo: rename editvisualndrawing to insertvisualndrawing though it can be used to edit
// when a drawing is selected and button clicked (also consider the case of not found drawings)
var myPluginSaveCallback = function(data) {
//console.log(data.drawing_id);
var entityElement = editor.document.createElement('drupal-visualn-drawing');
var attributes = data.tag_attributes;
for (var key in attributes) {
entityElement.setAttribute(key, attributes[key]);
}
editor.insertHtml(entityElement.getOuterHtml());
};
// Populate existing values of form.
var existingValues = {};
var drawing_id = getSelectionDrawingId(editor);
if (drawing_id) {
existingValues['data-visualn-drawing-id'] = drawing_id;
}
var dialogSettings = {
dialogClass: 'ui-dialog-visualn'
};
// Open dialog form.
Drupal.ckeditor.openDialog(editor,
Drupal.url('visualn_embed/form/drawing_embed_dialog/' + editor.config.drupal.format),
existingValues,
myPluginSaveCallback,
dialogSettings
);
}
});
editor.addCommand( 'configurevisualndrawing', {
allowedContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
requiredContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
modes: { wysiwyg : 1 },
canUndo: false,
//canUndo: true,
exec: function (editor, data) {
// @todo: this is a copy-paste form editdrawing method
var myPluginSaveCallback = function(data) {
//console.log(data.drawing_id);
// @todo: replace widget markup and element tag itself using values
// from the properties dialog
//console.log('drawing properties');
var entityElement = editor.document.createElement('drupal-visualn-drawing');
var attributes = data.tag_attributes;
for (var key in attributes) {
entityElement.setAttribute(key, attributes[key]);
}
editor.insertHtml(entityElement.getOuterHtml());
};
/*
var drawing_id = getSelectionDrawingId(editor);
// Populate existing values of form.
var existingValues = {
drawing_id: drawing_id
};
*/
var existingElement = getSelectedEmbeddedDrawing(editor);
// @todo: move into a method since it is reused also in other places
// Populate existing values of form.
var existingValues = {};
if (existingElement && existingElement.$ && existingElement.$.firstChild) {
var embedDOMElement = existingElement.$.firstChild;
// Populate array with the entity's current attributes.
var attribute = null, attributeName;
for (var key = 0; key < embedDOMElement.attributes.length; key++) {
attribute = embedDOMElement.attributes.item(key);
attributeName = attribute.nodeName.toLowerCase();
if (attributeName.substring(0, 15) === 'data-cke-saved-') {
continue;
}
existingValues[attributeName] = existingElement.data('cke-saved-' + attributeName) || attribute.nodeValue;
}
}
//var embed_button_id = data.id ? data.id : existingValues['data-embed-button'];
//console.log(existingValues);
var dialogSettings = {
dialogClass: 'ui-dialog-visualn'
};
// Open dialog form.
Drupal.ckeditor.openDialog(editor,
Drupal.url('visualn_embed/form/drawing_properties_dialog/' + editor.config.drupal.format),
existingValues,
myPluginSaveCallback,
dialogSettings
);
}
});
editor.widgets.add('drupalvisualndrawing', {
allowedContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
requiredContent: 'drupal-visualn-drawing[data-visualn-drawing-id]',
upcast: function (element) {
var attributes = element.attributes;
if (attributes['data-visualn-drawing-id'] === undefined) {
//if (attributes['data-entity-type'] === undefined || (attributes['data-entity-id'] === undefined && attributes['data-entity-uuid'] === undefined) || (attributes['data-view-mode'] === undefined && attributes['data-entity-embed-display'] === undefined)) {
return;
}
element.attributes.id = generateEmbedId();
return element;
},
init: function() {
var element = this.element;
var attributes = element.$.attributes;
// @todo: add check if not empty
var drawing_id = attributes['data-visualn-drawing-id'].value;
//var drawing_id = attributes['data-visualn-drawing-id'];
//console.log(drawing_id);
// set keys to generate ckeditor widget placeholder for the drawing
// @todo: rework widget placeholder template
var params = {};
var param_keys = {
align: 'data-align',
width: 'width',
height: 'height',
settings: 'data-visualn-drawing-settings'
};
$.each(param_keys, function(key, attr){
if (typeof attributes[attr] != 'undefined') {
params[key] = attributes[attr].value;
}
});
var entityEmbedPreview = Drupal.ajax({
base: element.getId(),
element: element.$,
// @todo: use 'widget' or 'placeholder' in the url instead of 'preview'
url: Drupal.url('visualn-drawing-embed/preview/' + editor.config.drupal.format + '/' + drawing_id + '?' + $.param(params)),
/*
url: Drupal.url('embed/preview/' + editor.config.drupal.format + '?' + $.param({
value: element.getOuterHtml()
})),
*/
progress: {type: 'none'},
// Use a custom event to trigger the call.
event: 'entity_embed_dummy_event'
});
entityEmbedPreview.execute();
//console.log(element.getId());
// @todo: see https://docs.ckeditor.com/ckeditor4/latest/guide/widget_sdk_tutorial_2.html
/*
if ( this.element.hasClass( 'align-left' ) )
this.setData( 'align', 'left' );
if ( this.element.hasClass( 'align-right' ) )
this.setData( 'align', 'right' );
if ( this.element.hasClass( 'align-center' ) )
this.setData( 'align', 'center' );
*/
},
data: function() {
// @todo: see https://docs.ckeditor.com/ckeditor4/latest/guide/widget_sdk_tutorial_2.html
if ( this.data.align )
this.element.addClass( 'align-' + this.data.align );
if (typeof this.element.$.attributes['data-align'] != 'undefined') {
var align = this.element.$.attributes['data-align'].value;
// @todo: exclude "none" value
this.element.addClass( 'align-' + align );
}
// @todo: enable manual resizing
}
});
editor.ui.addButton('Visualn-drawing-ckeditor-button', {
label: Drupal.t('VisualN Drawing'),
// Note that we use the original image2 command!
command: 'editdrawing',
icon: this.path + 'images/icon.png'
});
// Register context menu option for editing widget.
if (editor.contextMenu) {
editor.addMenuGroup('drupalvisualndrawing');
editor.addMenuItem('editvisualndrawing', {
label: Drupal.t('Edit VisualN Drawing'),
icon: this.path + 'images/icon.png',
command: 'editvisualndrawing',
group: 'drupalvisualndrawing'
});
editor.addMenuItem('previewvisualndrawing', {
label: Drupal.t('Open Drawing Preview'),
// @todo: set icon image path
//icon: this.path + 'images/icon.png',
command: 'previewvisualndrawing',
group: 'drupalvisualndrawing'
});
editor.addMenuItem('replacevisualndrawing', {
label: Drupal.t('Choose other...'),
// @todo: set icon image path
//icon: this.path + 'images/icon.png',
command: 'replacevisualndrawing',
group: 'drupalvisualndrawing'
});
editor.addMenuItem('configurevisualndrawing', {
label: Drupal.t('Drawing Properties'),
// @todo: set icon image path
//icon: this.path + 'images/icon.png',
command: 'configurevisualndrawing',
group: 'drupalvisualndrawing'
});
// @todo:
editor.contextMenu.addListener(function(element) {
if (isEditableDrawingWidget(editor, element)) {
return { 'editvisualndrawing' : CKEDITOR.TRISTATE_OFF, 'previewvisualndrawing' : CKEDITOR.TRISTATE_OFF, 'replacevisualndrawing' : CKEDITOR.TRISTATE_OFF, 'configurevisualndrawing' : CKEDITOR.TRISTATE_OFF };
}
});
}
// Execute widget editing action on double click.
editor.on('doubleclick', function (evt) {
// @todo: Do nothing for now, after properties change, the
// new widget markup is appended to the original one instead
// of replacing it. This seems to be 'doubleclick' ckeditor
// library bug since it works well e.g. on 'dragend' event.
return;
editor.execCommand('configurevisualndrawing');
//evt.editor.commands.configurevisualndrawing.exec(evt.data);
// @todo: open 'edit' dialog
/*
var element = getSelectedEmbeddedEntity(editor) || evt.data.element;
if (isEditableEntityWidget(editor, element)) {
editor.execCommand('editdrawing');
}
*/
});
/*
insertContent = function(html) {
editor.insertHtml(html);
}
*/
},
init: function (editor) {
},
});
function generateEmbedId() {
if (typeof generateEmbedId.counter == 'undefined') {
generateEmbedId.counter = 0;
}
return 'visualn-drawing-embed-' + generateEmbedId.counter++;
}
function getSelectionDrawingId(editor) {
var selection = editor.getSelection();
var selectedElement = selection.getSelectedElement();
var existingElement = selectedElement;
//console.log(selectedElement);
var drawing_id = null;
if (existingElement && existingElement.$ && existingElement.$.firstChild) {
var embedDOMElement = existingElement.$.firstChild;
// Populate array with the entity's current attributes.
var attribute = null, attributeName;
for (var key = 0; key < embedDOMElement.attributes.length; key++) {
attribute = embedDOMElement.attributes.item(key);
attributeName = attribute.nodeName.toLowerCase();
if (attributeName === 'data-visualn-drawing-id') {
//var drawing_id = existingElement.data('visualn-drawing-id');
var drawing_id = attribute.nodeValue;
//console.log(drawing_id);
}
/*
if (attributeName.substring(0, 15) === 'data-cke-saved-') {
continue;
}
existingValues[attributeName] = existingElement.data('cke-saved-' + attributeName) || attribute.nodeValue;
*/
}
}
return drawing_id;
}
/**
* Get the surrounding drupalentity widget element.
*
* @param {CKEDITOR.editor} editor
*/
function getSelectedEmbeddedDrawing(editor) {
//function getSelectedEmbeddedEntity(editor) {
var selection = editor.getSelection();
var selectedElement = selection.getSelectedElement();
return selectedElement;
/*
if (isEditableDrawingWidget(editor, selectedElement)) {
return selectedElement;
}
return null;
*/
}
/**
* Checks if the given element is an editable drupalentity widget.
*
* @param {CKEDITOR.editor} editor
* @param {CKEDITOR.htmlParser.element} element
*/
function isEditableDrawingWidget (editor, element) {
var widget = editor.widgets.getByElement(element, true);
if (!widget || widget.name !== 'drupalvisualndrawing') {
return false;
}
return true;
/*
var button = $(element.$.firstChild).attr('data-embed-button');
if (!button) {
// If there was no data-embed-button attribute, not editable.
return false;
}
// The button itself must be valid.
return editor.config.DrupalEntity_buttons.hasOwnProperty(button);
*/
}
})(jQuery, Drupal, drupalSettings, CKEDITOR);
