diff --git a/includes/ckeditor.lib.inc b/includes/ckeditor.lib.inc index a6f3ad5..f801455 100644 --- a/includes/ckeditor.lib.inc +++ b/includes/ckeditor.lib.inc @@ -820,14 +820,9 @@ function ckeditor_profile_settings_compile($global_profile, $profile) { $settings['loadPlugins'] = ckeditor_plugins_render($conf['loadPlugins']); if (array_key_exists('media', $settings['loadPlugins']) && module_exists('media')) { - module_load_include('inc', 'media', 'includes/media.browser'); - $javascript = media_browser_js(); - foreach ($javascript as $key => $definitions) { - foreach ($definitions as $definition) { - $function = 'drupal_add_' . $key; - call_user_func_array($function, $definition); - } - } + module_load_include('inc', 'media', 'wysiwyg_plugins/media'); + media_include_browser_js(); + drupal_add_js(drupal_get_path('module', 'media') . '/js/media.filter.js', array('scope' => 'footer', 'weight' => -21)); drupal_add_js(drupal_get_path('module', 'ckeditor') . '/plugins/media/library.js', array('scope' => 'footer', 'weight' => -20)); } } diff --git a/plugins/media/icons/media.png b/plugins/media/icons/media.png new file mode 100644 index 0000000000000000000000000000000000000000..272871f94b7e59d21677a41e3edd0678dedfcf21 GIT binary patch literal 2918 zcmV-s3z_tZP)X+uL$Nkc;* zP;zf(X>4Tx07wm;mUmQB*%pV-y*Itk5+Wca^cs2zAksTX6$DXM^`x7XQc?|s+0 z08spb1j2M!0f022SQPH-!CVp(%f$Br7!UytSOLJ{W@ZFO_(THK{JlMynW#v{v-a*T zfMmPdEWc1DbJqWVks>!kBnAKqMb$PuekK>?0+ds;#ThdH1j_W4DKdsJG8Ul;qO2n0 z#IJ1jr{*iW$(WZWsE0n`c;fQ!l&-AnmjxZO1uWyz`0VP>&nP`#itsL#`S=Q!g`M=rU9)45( zJ;-|dRq-b5&z?byo>|{)?5r=n76A4nTALlSzLiw~v~31J<>9PP?;rs31pu_(obw)r zY+jPY;tVGXi|p)da{-@gE-UCa`=5eu%D;v=_nFJ?`&K)q7e9d`Nfk3?MdhZarb|T3 z%nS~f&t(1g5dY)AIcd$w!z`Siz!&j_=v7hZlnI21XuE|xfmo0(WD10T)!}~_HYW!e zew}L+XmwuzeT6wtxJd`dZ#@7*BLgIEKY9Xv>st^p3dp{^Xswa2bB{85{^$B13tWnB z;Y>jyQ|9&zk7RNsqAVGs--K+z0uqo1bf5|}fi5rtEMN^BfHQCd-XH*kfJhJnmIE$G z0%<@5vOzxB0181d*a3EfYH$G5fqKvcPJ%XY23!PJzzuK<41h;K3WmW;Fah3yX$XSw z5EY_9s*o0>51B&N5F1(uc|$=^I1~fLLy3?Ol0f;;Ca4%HgQ}rJP(Ab`bQ-z{U4#0d z2hboi2K@njgb|nm(_szR0JebHusa+GN5aeCM0gdP2N%HG;Yzp`J`T6S7vUT504#-H z!jlL<$Or?`Mpy_N@kBz9SR?@vA#0H$qyni$nvf2p8@Y{0k#Xb$28W?xm>3qu8RLgp zjNxKdVb)?wFx8l2m{v>|<~C*!GlBVnrDD~wrdTJeKXwT=5u1%I#8zOBU|X=4u>;s) z>^mF|$G{ol9B_WP7+f-LHLe7=57&&lfa}8z;U@8Tyei%l?}87(bMRt(A-)QK9Dg3) zj~~XrCy)tR1Z#p1A(kK{Y$Q|=8VKhI{e%(1G*N-5Pjn)N5P8I0VkxnX*g?EW941ba z6iJ387g8iCnY4jaNopcpCOsy-A(P2EWJhusSwLP-t|XrzUnLKcKTwn?CKOLf97RIe zPB}`sKzTrUL#0v;sBY9)s+hW+T2H-1eM)^VN0T#`^Oxhvt&^*fYnAJldnHel*Ozyf zUoM{~Um<@={-*r60#U(0!Bc^wuvVc);k3d%g-J!4qLpHZVwz%!VuRu}#Ze`^l7W)9 z5>Kf>>9Eozr6C$Z)1`URxU@~QI@)F0FdauXr2Es8>BaOP=)Lp_WhG@>R;lZ?BJkMlIuMhw8ApiF&yDYW2hFJ?fJhni{?u z85&g@mo&yT8JcdI$(rSw=QPK(Xj%)k1X|@<=e1rim6`6$RAwc!i#egKuI;BS(LSWz zt39n_sIypSqfWEV6J3%nTQ@-4i zi$R;gsG*9XzhRzXqv2yCs*$VFDx+GXJH|L;wsDH_KI2;^u!)^Xl1YupO;gy^-c(?^ z&$Q1BYvyPsG^;hc$D**@Sy`+`)}T4VJji^bd7Jqw3q6Zii=7tT7GEswEK@D(EFW1Z zSp`^awCb?>!`j4}Yh7b~$A)U-W3$et-R8BesV(1jzwLcHnq9En7Q0Tn&-M=XBKs!$ zF$X<|c!#|X_tWYh)GZit z(Q)Cp9CDE^WG;+fcyOWARoj*0TI>4EP1lX*cEoMO-Pk?Z{kZ!p4@(b`M~lalr<3Oz z&kJ6Nm#vN_+kA5{dW4@^Vjg_`q%qU1ULk& z3Fr!>1V#i_2R;ij2@(Z$1jE4r!MlPVFVbHmT+|iPIq0wy5aS{>yK?9ZAjVh%SOwMWgFjair&;wpi!{CU}&@N=Eg#~ zLQ&zpEzVmGY{hI9Z0+4-0xS$$Xe-OToc?Y*V;rTcf_ zb_jRe-RZjXSeas3UfIyD;9afd%<`i0x4T#DzE)vdabOQ=k7SRuGN`h>O0Q~1)u-yD z>VX=Mn&!Rgd$;YK+Q-}1zu#?t(*cbG#Ronf6db&N$oEidtwC+YVcg-Y!_VuY>bk#Y ze_ww@?MU&F&qswvrN_dLb=5o6*Egs)ls3YRlE$&)amR1{;Ppd$6RYV^Go!iq1UMl% z@#4q$AMc(FJlT1QeX8jv{h#)>&{~RGq1N2iiMFIRX?sk2-|2wUogK~{EkB$8eDsX= znVPf8XG_nK&J~=SIiGia@9y}|z3FhX{g&gcj=lwb=lWgyFW&aLedUh- zof`v-2Kw$UzI*>(+&$@i-u=-BsSjR1%z8NeX#HdC`Hh-Z(6xI-`hmHDqv!v)W&&nrf>M(RhcN6(D;jNN*%^u_SYjF;2ng}*8Ow)d6M ztDk;%`@Lsk$;9w$(d(H%O5UixIr`T2ZRcd@Yv%7WRCUvT{5;?Xed zeCO@)vQmnTpm=aFJsCqnp!vnY$V55Ac5fGF$&e|OOA zZ8}q}Ax*5#jzUU_3$e4?&z^JUQNZ3~!(Y9e4!oL5M#_L#*YnXTfw^{djJ5F{ROuC2 zMInSF5@S>vxV!@>qY0nT$H;vV*^7-1JV`|5fIet<8 literal 0 HcmV?d00001 diff --git a/plugins/media/images/icon.gif b/plugins/media/images/icon.gif deleted file mode 100644 index bf9b501c3dd5a10dcbc33ce2491afe1ea55fb737..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126 zcmZ?wbhEHb6krfwSj52a|NsAk55FHcaG;@~fdR|~l0cyNlZBC;ft5iA!~v;gV0Mn! zb;sM_)DNZ&KjMOxwKKS-').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; + 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); +} )();