').append( element.eq(0).clone() ).html();
- },
-
- addImageAttributes: function (imgElement, fid, view_mode, additional) {
- imgElement.addClass('media-image');
-
- this.forceAttributesIntoClass(imgElement, fid, view_mode, additional);
- },
-
- /**
- * Due to problems handling wrapping divs in ckeditor, this is needed.
- *
- * Going forward, if we don't care about supporting other editors
- * we can use the fakeobjects plugin to ckeditor to provide cleaner
- * transparency between what Drupal will output
, for now though, we're going to remove all the stuff surrounding the images.
- *
- * @param String formattedMedia
- * Element containing the image
- *
- * @return HTML of
tag inside formattedMedia
- */
- stripDivs: function (formattedMedia) {
- // Check to see if the image tag has divs to strip
- var stripped = null;
- if ($(formattedMedia).is('img')) {
- stripped = this.outerHTML($(formattedMedia));
- } else {
- stripped = this.outerHTML($('img', $(formattedMedia)));
- }
- // This will fail if we pass the img tag without anything wrapping it, like we do when re-enabling ckeditor
- return stripped;
- },
-
- /**
- * Attach function, called when a rich text editor loads.
- * This finds all [[tags]] and replaces them with the html
- * that needs to show in the editor.
- *
- */
- attach: function (content, settings, instanceId) {
- var matches = content.match(/\[\[.*?\]\]/g);
- this.initializeTagMap();
- var tagmap = Drupal.settings.tagmap;
- if (matches) {
- var inlineTag = "";
- for (i = 0; i < matches.length; i++) {
- inlineTag = matches[i];
- if (tagmap[inlineTag]) {
- // This probably needs some work...
- // We need to somehow get the fid propogated here.
- // We really want to
- var tagContent = tagmap[inlineTag];
- var mediaMarkup = this.stripDivs(tagContent); // THis is
..
-
- var _tag = inlineTag;
- _tag = _tag.replace('[[','');
- _tag = _tag.replace(']]','');
- mediaObj = JSON.parse(_tag);
-
- var imgElement = $(mediaMarkup);
- this.addImageAttributes(imgElement, mediaObj.fid, mediaObj.view_mode);
- var toInsert = this.outerHTML(imgElement);
- content = content.replace(inlineTag, toInsert);
- }
- else {
- debug.debug("Could not find content for " + inlineTag);
- }
- }
- }
- return content;
- },
-
- /**
- * Detach function, called when a rich text editor detaches
- */
- detach: function (content, settings, instanceId) {
- var content = $('
' + content + '
');
- $('img.media-image',content).each(function (elem) {
- var tag = Drupal.settings.ckeditor.plugins['media'].createTag($(this));
- $(this).replaceWith(tag);
- var newContent = content.html();
- var tagContent = $('
').append($(this)).html();
- Drupal.settings.tagmap[tag] = tagContent;
+ insertMediaFile: function (mediaFile, formattedMedia, ckeditorInstance) {
+ // Customization of Drupal.media.filter.registerNewElement().
+ var element = Drupal.media.filter.create_element(formattedMedia.html, {
+ fid: mediaFile.fid,
+ view_mode: formattedMedia.type,
+ attributes: formattedMedia.options
});
- return content.html();
- },
-
- /**
- * @param jQuery imgNode
- * Image node to create tag from
- */
- createTag: function (imgNode) {
- // Currently this is the
itself
- // Collect all attribs to be stashed into tagContent
- var mediaAttributes = {};
- var imgElement = imgNode[0];
- var sorter = [];
- // @todo: this does not work in IE, width and height are always 0.
- for (i=0; i< imgElement.attributes.length; i++) {
- var attr = imgElement.attributes[i];
- if (attr.specified == true) {
- if (attr.name !== 'class') {
- sorter.push(attr);
- }
- else {
- // Exctract expando properties from the class field.
- var attributes = this.getAttributesFromClass(attr.value);
- for (var name in attributes) {
- if (attributes.hasOwnProperty(name)) {
- var value = attributes[name];
- if (value.type && value.type === 'attr') {
- sorter.push(value);
- }
- }
- }
- }
- }
- }
+ this.forceAttributesIntoClass(
+ element,
+ mediaFile.fid,
+ formattedMedia.type,
+ formattedMedia.options
+ );
- sorter.sort(this.sortAttributes);
+ // Use own wrapper element to be able to properly deal with selections.
+ // Check prepareDataForWysiwygMode() in plugin.js for details.
+ var wysiwygHTML = Drupal.media.filter.getWysiwygHTML(element);
- for (var prop in sorter) {
- mediaAttributes[sorter[prop].name] = sorter[prop].value;
- }
-
- // The following 5 ifs are dedicated to IE7
- // If the style is null, it is because IE7 can't read values from itself
- if (jQuery.browser.msie && jQuery.browser.version == '7.0') {
- if (mediaAttributes.style === "null") {
- var imgHeight = imgNode.css('height');
- var imgWidth = imgNode.css('width');
- mediaAttributes.style = {
- height: imgHeight,
- width: imgWidth
- }
- if (!mediaAttributes['width']) {
- mediaAttributes['width'] = imgWidth;
- }
- if (!mediaAttributes['height']) {
- mediaAttributes['height'] = imgHeight;
- }
- }
- // If the attribute width is zero, get the CSS width
- if (Number(mediaAttributes['width']) === 0) {
- mediaAttributes['width'] = imgNode.css('width');
- }
- // IE7 does support 'auto' as a value of the width attribute. It will not
- // display the image if this value is allowed to pass through
- if (mediaAttributes['width'] === 'auto') {
- delete mediaAttributes['width'];
- }
- // If the attribute height is zero, get the CSS height
- if (Number(mediaAttributes['height']) === 0) {
- mediaAttributes['height'] = imgNode.css('height');
- }
- // IE7 does support 'auto' as a value of the height attribute. It will not
- // display the image if this value is allowed to pass through
- if (mediaAttributes['height'] === 'auto') {
- delete mediaAttributes['height'];
- }
- }
+ // Insert element. Use CKEDITOR.dom.element.createFromHtml to ensure our
+ // custom wrapper element is preserved.
+ wysiwygHTML = wysiwygHTML.replace(/(.*?)/gi, '
$2');
+ var editorElement = CKEDITOR.dom.element.createFromHtml(wysiwygHTML);
+ ckeditorInstance.insertElement(editorElement);
- // Remove elements from attribs using the blacklist
- for (var blackList in Drupal.settings.media.blacklist) {
- delete mediaAttributes[Drupal.settings.media.blacklist[blackList]];
+ // Initialize widget on our html if possible.
+ if (parseFloat(CKEDITOR.version) >= 4.3) {
+ ckeditorInstance.widgets.initOn( editorElement, 'mediabox' );
}
- tagContent = {
- "type": 'media',
- // @todo: This will be selected from the format form
- "view_mode": attributes['view_mode'].value,
- "fid" : attributes['fid'].value,
- "attributes": mediaAttributes
- };
- return '[[' + JSON.stringify(tagContent) + ']]';
},
/**
- * Forces custom attributes into the class field of the specified image.
- *
- * Due to a bug in some versions of Firefox
- * (http://forums.mozillazine.org/viewtopic.php?f=9&t=1991855), the
- * custom attributes used to share information about the image are
- * being stripped as the image markup is set into the rich text
- * editor. Here we encode these attributes into the class field so
- * the data survives.
- *
- * @param imgElement
- * The image
- * @fid
- * The file id.
- * @param view_mode
- * The view mode.
- * @param additional
- * Additional attributes to add to the image.
- */
+ * Forces custom attributes into the class field of the specified image.
+ *
+ * Due to a bug in some versions of Firefox
+ * (http://forums.mozillazine.org/viewtopic.php?f=9&t=1991855), the
+ * custom attributes used to share information about the image are
+ * being stripped as the image markup is set into the rich text
+ * editor. Here we encode these attributes into the class field so
+ * the data survives.
+ *
+ * @param imgElement
+ * The image
+ * @fid
+ * The file id.
+ * @param view_mode
+ * The view mode.
+ * @param additional
+ * Additional attributes to add to the image.
+ */
forceAttributesIntoClass: function (imgElement, fid, view_mode, additional) {
var wysiwyg = imgElement.attr('wysiwyg');
if (wysiwyg) {
@@ -295,16 +113,16 @@
},
/**
- * Retrieves encoded attributes from the specified class string.
- *
- * @param classString
- * A string containing the value of the class attribute.
- * @return
- * An array containing the attribute names as keys, and an object
- * with the name, value, and attribute type (either 'attr' or
- * 'img', depending on whether it is an image attribute or should
- * be it the attributes section)
- */
+ * Retrieves encoded attributes from the specified class string.
+ *
+ * @param classString
+ * A string containing the value of the class attribute.
+ * @return
+ * An array containing the attribute names as keys, and an object
+ * with the name, value, and attribute type (either 'attr' or
+ * 'img', depending on whether it is an image attribute or should
+ * be it the attributes section)
+ */
getAttributesFromClass: function (classString) {
var actualClasses = [];
var otherAttributes = [];
diff --git a/plugins/media/plugin.js b/plugins/media/plugin.js
index 363f843..7ff60c4 100644
--- a/plugins/media/plugin.js
+++ b/plugins/media/plugin.js
@@ -4,15 +4,17 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
- * @file Plugin for inserting images from Drupal media module
- */
+* @file Plugin for inserting images from Drupal media module
+*/
( function() {
- CKEDITOR.plugins.add( 'media',
- {
+ var mediaPluginDefinition = {
+ icons: 'media',
+ requires: ['button'],
+
// Wrap Drupal plugin in a proxy plugin.
- init: function(editor)
- {
- var pluginCommand = {
+ init: function(editor){
+ editor.addCommand( 'media',
+ {
exec: function (editor) {
var data = {
format: 'html',
@@ -30,6 +32,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
if (CKEDITOR.env.ie) {
data.content = selection.getNative().createRange().text;
}
+
else {
data.content = selection.getNative().toString();
}
@@ -41,18 +44,156 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
}
Drupal.settings.ckeditor.plugins['media'].invoke(data, Drupal.settings.ckeditor.plugins['media'], editor.name);
}
- };
- editor.addCommand( 'media', pluginCommand );
+ });
editor.ui.addButton( 'Media',
{
label: 'Add media',
- command: 'media',
- icon: this.path + 'images/icon.gif'
+ command: 'media'
});
- }
- });
-} )();
+ var ckeditorversion = parseFloat(CKEDITOR.version);
+
+ // Because the media comment wrapper don't work well for CKEditor we
+ // replace them by using a custom mediawrapper element.
+ // Instead having
+ //
+ // We wrap the placeholder with
+ //
+ // That way we can deal better with selections - see selectionChange.
+ CKEDITOR.dtd['mediawrapper'] = CKEDITOR.dtd;
+ CKEDITOR.dtd.$blockLimit['mediawrapper'] = 1;
+ CKEDITOR.dtd.$inline['mediawrapper'] = 1;
+ CKEDITOR.dtd.$nonEditable['mediawrapper'] = 1;
+ if (ckeditorversion >= 4.1) {
+ // Register allowed tag for advanced filtering.
+ editor.filter.allow( 'mediawrapper[!data]', 'MediaWrapper' );
+ // Objects should be selected as a whole in the editor.
+ CKEDITOR.dtd.$object['mediawrapper'] = 1;
+ }
+ function prepareDataForWysiwygMode(data) {
+ data = Drupal.media.filter.replaceTokenWithPlaceholder(data);
+ data = data.replace(/(.*?)/gi, '
$2');
+ return data;
+ }
+ function prepareDataForSourceMode(data) {
+ data = data.replace(/
(.*?)<\/mediawrapper>/gi, '$2');
+ data = Drupal.media.filter.replacePlaceholderWithToken(data);
+ return data;
+ }
+
+ // Ensure the tokens are replaced by placeholders while editing.
+ if (ckeditorversion >= 4.3) {
+ // CKEditor >=4.3 behaviour.
+ editor.widgets.add( 'mediabox',
+ {
+ button: 'Create a mediabox',
+ template: '',
+ editables: {},
+ allowedContent: '*',
+ upcast: function( element ) {
+ if (element.name != 'mediawrapper') {
+ // Ensure media tokens are converted to media placeholdes.
+ element.setHtml(prepareDataForWysiwygMode(element.getHtml()));
+ }
+ return element.name == 'mediawrapper';
+ },
+ downcast: function( widgetElement ) {
+ var token = prepareDataForSourceMode(widgetElement.getOuterHtml());
+ return new CKEDITOR.htmlParser.text(token);
+ return element.name == 'mediawrapper';
+ }
+ });
+ }
+ else if (ckeditorversion >= 4) {
+ // CKEditor >=4.0, <4.3 behaviour.
+ editor.on('setData', function( event ) {
+ event.data.dataValue = prepareDataForWysiwygMode(event.data.dataValue);
+ });
+ }
+ else {
+ // CKEditor >=3.6 behaviour.
+ editor.on( 'beforeSetMode', function( event, data ) {
+ event.removeListener();
+ var wysiwyg = editor._.modes[ 'wysiwyg' ];
+ var source = editor._.modes[ 'source' ];
+ wysiwyg.loadData = CKEDITOR.tools.override( wysiwyg.loadData, function( org )
+ {
+ return function( data ) {
+ return ( org.call( this, prepareDataForWysiwygMode(data)) );
+ };
+ } );
+ source.loadData = CKEDITOR.tools.override( source.loadData, function( org )
+ {
+ return function( data ) {
+ return ( org.call( this, prepareDataForSourceMode(data) ) );
+ };
+ } );
+ });
+ }
+
+ // Provide alternative to the widget functionality introduced in 4.3.
+ if (ckeditorversion < 4.3) {
+ // Ensure tokens instead the html element is saved.
+ editor.on('getData', function( event ) {
+ event.data.dataValue = prepareDataForSourceMode(event.data.dataValue);
+ });
+ // Ensure our enclosing wrappers are always included in the selection.
+ editor.on('selectionChange', function( event ) {
+ var ranges = editor.getSelection().getRanges().createIterator();
+ var newRanges = [];
+ var currRange;
+ while(currRange = ranges.getNextRange()){
+ var commonAncestor = currRange.getCommonAncestor(false);
+ if (commonAncestor && commonAncestor.getName() == 'mediawrapper') {
+ var range = new CKEDITOR.dom.range( editor.document );
+ if (currRange.collapsed === true) {
+ // Don't allow selection within the wrapper element.
+ if (currRange.startOffset == 0) {
+ // While v3 plays nice with setting start and end to avoid
+ // editing within the media wrapper element, v4 ignores that.
+ // Thus we try to move the cursor further away.
+ if (parseInt(CKEDITOR.version) > 3) {
+ range.setStart(commonAncestor.getPrevious());
+ range.setEnd(commonAncestor.getPrevious());
+ }
+ else {
+ range.setStartBefore(commonAncestor);
+ }
+ }
+ else {
+ // While v3 plays nice with setting start and end to avoid
+ // editing within the media wrapper element, v4 ignores that.
+ // Thus we try to move the cursor further away.
+ if (parseInt(CKEDITOR.version) > 3) {
+ range.setStart(commonAncestor.getNext(), 1);
+ range.setEnd(commonAncestor.getNext(), 1);
+ }
+ else {
+ range.setStartAfter(commonAncestor);
+ }
+ }
+ }
+ else {
+ // Always select the whole wrapper element.
+ range.setStartBefore(commonAncestor);
+ range.setEndAfter(commonAncestor);
+ }
+ newRanges.push(range);
+ }
+ }
+ if (newRanges.length) {
+ editor.getSelection().selectRanges(newRanges);
+ }
+ });
+ }
+ }
+ }
+ // Add dependency to widget plugin if possible.
+ if (parseFloat(CKEDITOR.version) >= 4.3) {
+ mediaPluginDefinition.requires.push('widget');
+ }
+ CKEDITOR.plugins.add( 'media', mediaPluginDefinition);
+} )();