Update to Drupal 8.0.0-rc3. For more information, see https://www.drupal.org/node/2608078
This commit is contained in:
parent
6419a031d7
commit
4afb23bbd3
762 changed files with 20080 additions and 6368 deletions
|
@ -29,50 +29,55 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Override requiredContent & allowedContent.
|
||||
widgetDefinition.requiredContent = new CKEDITOR.style({
|
||||
element: 'img',
|
||||
styles: {},
|
||||
attributes: {
|
||||
'alt': '',
|
||||
'src': '',
|
||||
'width': '',
|
||||
'height': '',
|
||||
'data-entity-type': '',
|
||||
'data-entity-uuid': ''
|
||||
}
|
||||
});
|
||||
var allowedContentDefinition = {
|
||||
element: 'img',
|
||||
styles: {},
|
||||
attributes: {
|
||||
'!data-entity-type': '',
|
||||
'!data-entity-uuid': ''
|
||||
// First, convert requiredContent & allowedContent from the string
|
||||
// format that image2 uses for both to formats that are better suited
|
||||
// for extending, so that both this basic drupalimage plugin and Drupal
|
||||
// modules can easily extend it.
|
||||
// @see http://docs.ckeditor.com/#!/api/CKEDITOR.filter.allowedContentRules
|
||||
// Mapped from image2's allowedContent. Unlike image2, we don't allow
|
||||
// <figure>, <figcaption>, <div> or <p> in our downcast, so we omit
|
||||
// those. For the <img> tag, we list all attributes it lists, but omit
|
||||
// the classes, because the listed classes are for alignment, and for
|
||||
// alignment we use the data-align attribute.
|
||||
widgetDefinition.allowedContent = {
|
||||
img: {
|
||||
attributes: {
|
||||
'!src': true,
|
||||
'!alt': true,
|
||||
'width': true,
|
||||
'height': true
|
||||
},
|
||||
classes: {}
|
||||
}
|
||||
};
|
||||
var imgAttributes = widgetDefinition.allowedContent.img.attributes.split(/\s*,\s*/);
|
||||
for (var i = 0; i < imgAttributes.length; i++) {
|
||||
allowedContentDefinition.attributes[imgAttributes[i]] = '';
|
||||
}
|
||||
if (widgetDefinition.allowedContent.img.classes) {
|
||||
allowedContentDefinition.attributes['class'] = widgetDefinition.allowedContent.img.classes.split(/\s*,\s*/).join(' ');
|
||||
}
|
||||
if (widgetDefinition.allowedContent.img.styles) {
|
||||
var imgStyles = widgetDefinition.allowedContent.img.styles.split(/\s*,\s*/);
|
||||
for (var j = 0; j < imgStyles.length; j++) {
|
||||
allowedContentDefinition.styles[imgStyles[j]] = '';
|
||||
// Mapped from image2's requiredContent: "img[src,alt]". This does not
|
||||
// use the object format unlike above, but a CKEDITOR.style instance,
|
||||
// because requiredContent does not support the object format.
|
||||
// @see https://www.drupal.org/node/2585173#comment-10456981
|
||||
widgetDefinition.requiredContent = new CKEDITOR.style({
|
||||
element: 'img',
|
||||
attributes: {
|
||||
src: '',
|
||||
alt: ''
|
||||
}
|
||||
}
|
||||
widgetDefinition.allowedContent = new CKEDITOR.style(allowedContentDefinition);
|
||||
});
|
||||
|
||||
// Override the 'link' part, to completely disable image2's link
|
||||
// support: http://dev.ckeditor.com/ticket/11341.
|
||||
widgetDefinition.parts.link = 'This is a nonsensical selector to disable this functionality completely';
|
||||
// Extend requiredContent & allowedContent.
|
||||
// CKEDITOR.style is an immutable object: we cannot modify its
|
||||
// definition to extend requiredContent. Hence we get the definition,
|
||||
// modify it, and pass it to a new CKEDITOR.style instance.
|
||||
var requiredContent = widgetDefinition.requiredContent.getDefinition();
|
||||
requiredContent.attributes['data-entity-type'] = '';
|
||||
requiredContent.attributes['data-entity-uuid'] = '';
|
||||
widgetDefinition.requiredContent = new CKEDITOR.style(requiredContent);
|
||||
widgetDefinition.allowedContent.img.attributes['!data-entity-type'] = true;
|
||||
widgetDefinition.allowedContent.img.attributes['!data-entity-uuid'] = true;
|
||||
|
||||
// Override downcast(): since we only accept <img> in our upcast method,
|
||||
// the element is already correct. We only need to update the element's
|
||||
// data-entity-uuid attribute.
|
||||
widgetDefinition.downcast = function (element) {
|
||||
element.attributes['data-entity-type'] = this.data['data-entity-type'];
|
||||
element.attributes['data-entity-uuid'] = this.data['data-entity-uuid'];
|
||||
};
|
||||
|
||||
|
@ -176,6 +181,18 @@
|
|||
return widget;
|
||||
};
|
||||
};
|
||||
|
||||
var originalInit = widgetDefinition.init;
|
||||
widgetDefinition.init = function () {
|
||||
originalInit.call(this);
|
||||
|
||||
// Update data.link object with attributes if the link has been
|
||||
// discovered.
|
||||
// @see plugins/image2/plugin.js/init() in CKEditor; this is similar.
|
||||
if (this.parts.link) {
|
||||
this.setData('link', CKEDITOR.plugins.link.parseLinkAttributes(editor, this.parts.link));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Add a widget#edit listener to every instance of image2 widget in order
|
||||
|
@ -233,25 +250,86 @@
|
|||
}
|
||||
},
|
||||
|
||||
// Disable image2's integration with the link/drupallink plugins: don't
|
||||
// allow the widget itself to become a link. Support for that may be added
|
||||
// by an text filter that adds a data- attribute specifically for that.
|
||||
afterInit: function (editor) {
|
||||
if (editor.plugins.drupallink) {
|
||||
var cmd = editor.getCommand('drupallink');
|
||||
// Needs to be refreshed on selection changes.
|
||||
cmd.contextSensitive = 1;
|
||||
// Disable command and cancel event when the image widget is selected.
|
||||
cmd.on('refresh', function (evt) {
|
||||
var widget = editor.widgets.focused;
|
||||
if (widget && widget.name === 'image') {
|
||||
this.setState(CKEDITOR.TRISTATE_DISABLED);
|
||||
evt.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
linkCommandIntegrator(editor);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Integrates the drupalimage widget with the drupallink plugin.
|
||||
*
|
||||
* Makes images linkable.
|
||||
*
|
||||
* @param {CKEDITOR.editor} editor
|
||||
* A CKEditor instance.
|
||||
*/
|
||||
function linkCommandIntegrator(editor) {
|
||||
// Nothing to integrate with if the drupallink plugin is not loaded.
|
||||
if (!editor.plugins.drupallink) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Override default behaviour of 'drupalunlink' command.
|
||||
editor.getCommand('drupalunlink').on('exec', function (evt) {
|
||||
var widget = getFocusedWidget(editor);
|
||||
|
||||
// Override 'drupalunlink' only when link truly belongs to the widget. If
|
||||
// wrapped inline widget in a link, let default unlink work.
|
||||
// @see https://dev.ckeditor.com/ticket/11814
|
||||
if (!widget || !widget.parts.link) {
|
||||
return;
|
||||
}
|
||||
|
||||
widget.setData('link', null);
|
||||
|
||||
// Selection (which is fake) may not change if unlinked image in focused
|
||||
// widget, i.e. if captioned image. Let's refresh command state manually
|
||||
// here.
|
||||
this.refresh(editor, editor.elementPath());
|
||||
|
||||
evt.cancel();
|
||||
});
|
||||
|
||||
// Override default refresh of 'drupalunlink' command.
|
||||
editor.getCommand('drupalunlink').on('refresh', function (evt) {
|
||||
var widget = getFocusedWidget(editor);
|
||||
|
||||
if (!widget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note that widget may be wrapped in a link, which
|
||||
// does not belong to that widget (#11814).
|
||||
this.setState(widget.data.link || widget.wrapper.getAscendant('a') ?
|
||||
CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED);
|
||||
|
||||
evt.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the focused widget, if of the type specific for this plugin.
|
||||
*
|
||||
* @param {CKEDITOR.editor} editor
|
||||
* A CKEditor instance.
|
||||
*
|
||||
* @return {?CKEDITOR.plugins.widget}
|
||||
* The focused image2 widget instance, or null.
|
||||
*/
|
||||
function getFocusedWidget(editor) {
|
||||
var widget = editor.widgets.focused;
|
||||
|
||||
if (widget && widget.name === 'image') {
|
||||
return widget;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Expose an API for other plugins to interact with drupalimage widgets.
|
||||
CKEDITOR.plugins.drupalimage = {
|
||||
getFocusedWidget: getFocusedWidget
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, CKEDITOR);
|
||||
|
|
|
@ -50,15 +50,16 @@
|
|||
}
|
||||
}, true);
|
||||
|
||||
// Override requiredContent & allowedContent.
|
||||
// Extend requiredContent & allowedContent.
|
||||
// CKEDITOR.style is an immutable object: we cannot modify its
|
||||
// definition to extend requiredContent. Hence we get the definition,
|
||||
// modify it, and pass it to a new CKEDITOR.style instance.
|
||||
var requiredContent = widgetDefinition.requiredContent.getDefinition();
|
||||
requiredContent.attributes['data-align'] = '';
|
||||
requiredContent.attributes['data-caption'] = '';
|
||||
widgetDefinition.requiredContent = new CKEDITOR.style(requiredContent);
|
||||
var allowedContent = widgetDefinition.allowedContent.getDefinition();
|
||||
allowedContent.attributes['!data-align'] = '';
|
||||
allowedContent.attributes['!data-caption'] = '';
|
||||
widgetDefinition.allowedContent = new CKEDITOR.style(allowedContent);
|
||||
widgetDefinition.allowedContent.img.attributes['!data-align'] = true;
|
||||
widgetDefinition.allowedContent.img.attributes['!data-caption'] = true;
|
||||
|
||||
// Override allowedContent setting for the 'caption' nested editable.
|
||||
// This must match what caption_filter enforces.
|
||||
|
@ -71,10 +72,9 @@
|
|||
// data-caption attributes.
|
||||
var originalDowncast = widgetDefinition.downcast;
|
||||
widgetDefinition.downcast = function (element) {
|
||||
var img = originalDowncast.call(this, element);
|
||||
if (!img) {
|
||||
img = findElementByName(element, 'img');
|
||||
}
|
||||
var img = findElementByName(element, 'img');
|
||||
originalDowncast.call(this, img);
|
||||
|
||||
var caption = this.editables.caption;
|
||||
var captionHtml = caption && caption.getData();
|
||||
var attrs = img.attributes;
|
||||
|
@ -91,10 +91,14 @@
|
|||
attrs['data-align'] = this.data.align;
|
||||
}
|
||||
}
|
||||
attrs['data-entity-type'] = this.data['data-entity-type'];
|
||||
attrs['data-entity-uuid'] = this.data['data-entity-uuid'];
|
||||
|
||||
return img;
|
||||
// If img is wrapped with a link, we want to return that link.
|
||||
if (img.parent.name === 'a') {
|
||||
return img.parent;
|
||||
}
|
||||
else {
|
||||
return img;
|
||||
}
|
||||
};
|
||||
|
||||
// We want to upcast <img> elements to a DOM structure required by the
|
||||
|
@ -115,6 +119,11 @@
|
|||
|
||||
element = originalUpcast.call(this, element, data);
|
||||
var attrs = element.attributes;
|
||||
|
||||
if (element.parent.name === 'a') {
|
||||
element = element.parent;
|
||||
}
|
||||
|
||||
var retElement = element;
|
||||
var caption;
|
||||
|
||||
|
|
|
@ -13,17 +13,17 @@
|
|||
init: function (editor) {
|
||||
// Add the commands for link and unlink.
|
||||
editor.addCommand('drupallink', {
|
||||
allowedContent: new CKEDITOR.style({
|
||||
element: 'a',
|
||||
styles: {},
|
||||
attributes: {
|
||||
'!href': '',
|
||||
'target': ''
|
||||
allowedContent: {
|
||||
a: {
|
||||
attributes: {
|
||||
'!href': true,
|
||||
'target': true
|
||||
},
|
||||
classes: {}
|
||||
}
|
||||
}),
|
||||
},
|
||||
requiredContent: new CKEDITOR.style({
|
||||
element: 'a',
|
||||
styles: {},
|
||||
attributes: {
|
||||
href: ''
|
||||
}
|
||||
|
@ -31,6 +31,8 @@
|
|||
modes: {wysiwyg: 1},
|
||||
canUndo: true,
|
||||
exec: function (editor) {
|
||||
var drupalImageUtils = CKEDITOR.plugins.drupalimage;
|
||||
var focusedImageWidget = drupalImageUtils && drupalImageUtils.getFocusedWidget(editor);
|
||||
var linkElement = getSelectedLink(editor);
|
||||
var linkDOMElement = null;
|
||||
|
||||
|
@ -56,9 +58,30 @@
|
|||
existingValues[attributeName] = linkElement.data('cke-saved-' + attributeName) || attribute.nodeValue;
|
||||
}
|
||||
}
|
||||
// Or, if an image widget is focused, we're editing a link wrapping
|
||||
// an image widget.
|
||||
else if (focusedImageWidget && focusedImageWidget.data.link) {
|
||||
var url = focusedImageWidget.data.link.url;
|
||||
existingValues.href = url.protocol + url.url;
|
||||
}
|
||||
|
||||
// Prepare a save callback to be used upon saving the dialog.
|
||||
var saveCallback = function (returnValues) {
|
||||
// If an image widget is focused, we're not editing an independent
|
||||
// link, but we're wrapping an image widget in a link.
|
||||
if (focusedImageWidget) {
|
||||
var urlMatch = returnValues.attributes.href.match(urlRegex);
|
||||
focusedImageWidget.setData('link', {
|
||||
type: 'url',
|
||||
url: {
|
||||
protocol: urlMatch[1],
|
||||
url: urlMatch[2]
|
||||
}
|
||||
});
|
||||
editor.fire('saveSnapshot');
|
||||
return;
|
||||
}
|
||||
|
||||
editor.fire('saveSnapshot');
|
||||
|
||||
// Create a new link element if needed.
|
||||
|
@ -124,13 +147,14 @@
|
|||
editor.addCommand('drupalunlink', {
|
||||
contextSensitive: 1,
|
||||
startDisabled: 1,
|
||||
allowedContent: new CKEDITOR.style({
|
||||
element: 'a',
|
||||
attributes: {
|
||||
'!href': '',
|
||||
'target': ''
|
||||
allowedContent: {
|
||||
a: {
|
||||
attributes: {
|
||||
'!href': true,
|
||||
'target': true
|
||||
}
|
||||
}
|
||||
}),
|
||||
},
|
||||
requiredContent: new CKEDITOR.style({
|
||||
element: 'a',
|
||||
attributes: {
|
||||
|
@ -256,4 +280,57 @@
|
|||
return null;
|
||||
}
|
||||
|
||||
var urlRegex = /^((?:http|https):\/\/)?(.*)$/;
|
||||
|
||||
/**
|
||||
* The image2 plugin is currently tightly coupled to the link plugin: it
|
||||
* calls CKEDITOR.plugins.link.parseLinkAttributes().
|
||||
*
|
||||
* Drupal 8's CKEditor build doesn't include the 'link' plugin. Because it
|
||||
* includes its own link plugin that integrates with Drupal's dialog system.
|
||||
* So, to allow images to be linked, we need to duplicate the necessary subset
|
||||
* of the logic.
|
||||
*
|
||||
* @todo Remove once we update to CKEditor 4.5.5.
|
||||
* @see https://dev.ckeditor.com/ticket/13885
|
||||
*/
|
||||
CKEDITOR.plugins.link = CKEDITOR.plugins.link || {
|
||||
parseLinkAttributes: function (editor, element) {
|
||||
var href = (element && (element.data('cke-saved-href') || element.getAttribute('href'))) || '';
|
||||
var urlMatch = href.match(urlRegex);
|
||||
return {
|
||||
type: 'url',
|
||||
url: {
|
||||
protocol: urlMatch[1],
|
||||
url: urlMatch[2]
|
||||
}
|
||||
};
|
||||
},
|
||||
getLinkAttributes: function (editor, data) {
|
||||
var set = {};
|
||||
|
||||
var protocol = (data.url && typeof data.url.protocol !== 'undefined') ? data.url.protocol : 'http://';
|
||||
var url = (data.url && CKEDITOR.tools.trim(data.url.url)) || '';
|
||||
set['data-cke-saved-href'] = (url.indexOf('/') === 0) ? url : protocol + url;
|
||||
|
||||
// Browser need the "href" fro copy/paste link to work. (#6641)
|
||||
if (set['data-cke-saved-href']) {
|
||||
set.href = set['data-cke-saved-href'];
|
||||
}
|
||||
|
||||
// Remove all attributes which are not currently set.
|
||||
var removed = {};
|
||||
for (var s in set) {
|
||||
if (set.hasOwnProperty(s)) {
|
||||
delete removed[s];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
set: set,
|
||||
removed: CKEDITOR.tools.objectKeys(removed)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings, CKEDITOR);
|
||||
|
|
Reference in a new issue