Update to Drupal 8.0.0-rc3. For more information, see https://www.drupal.org/node/2608078

This commit is contained in:
Pantheon Automation 2015-11-04 11:11:27 -08:00 committed by Greg Anderson
parent 6419a031d7
commit 4afb23bbd3
762 changed files with 20080 additions and 6368 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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);