/** * @file * Attach Media ckeditor behaviors. */ (function ($) { Drupal.media = Drupal.media || {}; Drupal.settings.ckeditor.plugins['media'] = { /** * Initializes the tag map. */ initializeTagMap: function () { if (typeof Drupal.settings.tagmap == 'undefined') { Drupal.settings.tagmap = { }; } }, /** * Execute the button. */ invoke: function (data, settings, instanceId) { if (data.format == 'html') { Drupal.media.popups.mediaBrowser(function (mediaFiles) { Drupal.settings.ckeditor.plugins['media'].mediaBrowserOnSelect(mediaFiles, instanceId); }, settings['global']); } }, /** * Respond to the mediaBrowser's onSelect event. */ mediaBrowserOnSelect: function (mediaFiles, instanceId) { var mediaFile = mediaFiles[0]; var options = {}; Drupal.media.popups.mediaStyleSelector(mediaFile, function (formattedMedia) { Drupal.settings.ckeditor.plugins['media'].insertMediaFile(mediaFile, formattedMedia.type, formattedMedia.html, formattedMedia.options, CKEDITOR.instances[instanceId]); }, options); return; }, insertMediaFile: function (mediaFile, viewMode, formattedMedia, options, ckeditorInstance) { this.initializeTagMap(); // @TODO: the folks @ ckeditor have told us that there is no way // to reliably add wrapper divs via normal HTML. // There is some method of adding a "fake element" // But until then, we're just going to embed to img. // This is pretty hacked for now. // var imgElement = $(this.stripDivs(formattedMedia)); this.addImageAttributes(imgElement, mediaFile.fid, viewMode, options); var toInsert = this.outerHTML(imgElement); // Create an inline tag var inlineTag = Drupal.settings.ckeditor.plugins['media'].createTag(imgElement); // Add it to the tag map in case the user switches input formats Drupal.settings.tagmap[inlineTag] = toInsert; ckeditorInstance.insertHtml(toInsert); }, /** * Gets the HTML content of an element * * @param jQuery element */ outerHTML: function (element) { return $('
').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
* instead of just , 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; }); 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); } } } } } } sorter.sort(this.sortAttributes); 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']; } } // Remove elements from attribs using the blacklist for (var blackList in Drupal.settings.media.blacklist) { delete mediaAttributes[Drupal.settings.media.blacklist[blackList]]; } 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. */ forceAttributesIntoClass: function (imgElement, fid, view_mode, additional) { var wysiwyg = imgElement.attr('wysiwyg'); if (wysiwyg) { imgElement.addClass('attr__wysiwyg__' + wysiwyg); } var format = imgElement.attr('format'); if (format) { imgElement.addClass('attr__format__' + format); } var typeOf = imgElement.attr('typeof'); if (typeOf) { imgElement.addClass('attr__typeof__' + typeOf); } if (fid) { imgElement.addClass('img__fid__' + fid); } if (view_mode) { imgElement.addClass('img__view_mode__' + view_mode); } if (additional) { for (var name in additional) { if (additional.hasOwnProperty(name)) { if (name !== 'alt') { imgElement.addClass('attr__' + name + '__' + additional[name]); } } } } }, /** * 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 = []; var classes = classString.split(' '); var regexp = new RegExp('^(attr|img)__([^\S]*)__([^\S]*)$'); for (var index = 0; index < classes.length; index++) { var matches = classes[index].match(regexp); if (matches && matches.length === 4) { otherAttributes[matches[2]] = { name: matches[2], value: matches[3], type: matches[1] }; } else { actualClasses.push(classes[index]); } } if (actualClasses.length > 0) { otherAttributes['class'] = { name: 'class', value: actualClasses.join(' '), type: 'attr' }; } return otherAttributes; }, sortAttributes: function (a, b) { var nameA = a.name.toLowerCase(); var nameB = b.name.toLowerCase(); if (nameA < nameB) { return -1; } if (nameA > nameB) { return 1; } return 0; } }; })(jQuery);