diff --git a/js/media.filter.js b/js/media.filter.js index 47c9e77..1af106a 100644 --- a/js/media.filter.js +++ b/js/media.filter.js @@ -15,34 +15,44 @@ */ replaceTokenWithPlaceholder: function(content) { Drupal.media.filter.ensure_tagmap() - var tagmap = Drupal.settings.tagmap, - matches = content.match(/\[\[.*?\]\]/g), - media_definition; - - if (matches) { - for (var macro in tagmap) { - // We cant use indexOf because of IE. - var index = $.inArray(macro, matches); - if (index !== -1) { - var media_json = macro.replace('[[', '').replace(']]', ''); - - // Make sure that the media JSON is valid. - try { - media_definition = JSON.parse(media_json); - } - catch (err) { - media_definition = null; - } - if (media_definition) { - // Apply attributes. - var element = Drupal.media.filter.create_element(tagmap[macro], media_definition); - var markup = Drupal.media.filter.outerHTML(element); - - content = content.replace(macro, markup); - } + var matches = content.match(/\[\[.*?"type":"media".*?\]\]/g); + + if (!!matches) { + for (i in matches) { + var match = matches[i]; + + // Check if the macro exists in the tagmap. This ensures backwards + // compatibility with existing media and is moderately more efficient + // than re-building the element. + var media = Drupal.settings.tagmap[match]; + var media_json = match.replace('[[', '').replace(']]', ''); + + // Ensure that the media JSON is valid. + try { + var media_definition = JSON.parse(media_json); + } + catch (err) { + // @todo: error logging. + return; + } + + Drupal.media.filter.ensureSourceMap(); + + // Re-build the media if the macro has changed from the tagmap. + if (!media && media_definition.fid) { + var source = Drupal.settings.mediaSourceMap[media_definition.fid]; + media = document.createElement(source.tagName); + media.src = source.src; } + + // Apply attributes. + var element = Drupal.media.filter.create_element(media, media_definition); + var markup = Drupal.media.filter.outerHTML(element); + + content = content.replace(match, markup); } } + return content; }, @@ -52,22 +62,35 @@ */ replacePlaceholderWithToken: function(content) { Drupal.media.filter.ensure_tagmap(); - // Convert all xhtml markup to html for reliable matching/replacing. - content = content.replace(/[\s]\/\>/g, '>'); + var markup = document.createElement('div'); + markup.innerHTML = content; + + var matches = (!!markup) ? markup.getElementsByClassName('media-element') : []; + var placeholders = []; - // Re-build the macros in case any element has changed in the editor. - $('.media-element', content).each(function(i, element) { - var markup = Drupal.media.filter.outerHTML($(element)); - macro = Drupal.media.filter.create_macro($(element)); + // Rewrite the tagmap in case any of the macros have changed. + Drupal.settings.tagmap = {}; + + for (var i = 0; i < matches.length; i++) { + var macro = Drupal.media.filter.create_macro($(matches[i])); // Store the macro => html for more efficient rendering in // replaceTokenWithPlaceholder(). - Drupal.settings.tagmap[macro] = markup; - // Replace the media element with its macro. - content = content.replace(markup, macro); - }); + Drupal.settings.tagmap[macro] = matches[i]; - return content; + placeholders[i] = { + match: matches[i], + node: document.createTextNode(macro) + } + } + + // We have to loop through the placeholders separately because + // replaceChild will shift off the replacement from the NodeList. + for (i in placeholders) { + placeholders[i].match.parentNode.replaceChild(placeholders[i].node, placeholders[i].match); + } + + return markup.innerHTML; }, /** @@ -97,8 +120,8 @@ }, /** - * Serializes file information as a url-encoded JSON object and stores it as a - * data attribute on the html element. + * Serializes file information as a url-encoded JSON object and stores it + * as a data attribute on the html element. * * @param html (string) * A html element to be used to represent the inserted media element. @@ -107,12 +130,18 @@ */ create_element: function (html, info) { if ($('
').append(html).text().length === html.length) { - // Element is not an html tag. Surround it in a span element - // so we can pass the file attributes. + // Element is not an html tag. Surround it in a span element so we can + // pass the file attributes. html = '' + html + ''; } var element = $(html); + // Parse out link wrappers. They will be re-applied when the image is + // rendered on the front-end. + if (element.is('a')) { + element = element.children(); + } + // Move attributes from the file info array to the placeholder element. if (info.attributes) { $.each(Drupal.media.filter.allowed_attributes(), function(i, a) { @@ -121,6 +150,13 @@ } }); delete(info.attributes); + + // Store information to rebuild the element later, if necessary. + Drupal.media.filter.ensureSourceMap(); + Drupal.settings.mediaSourceMap[info.fid] = { + tagName: element[0].tagName, + src: element[0].src + } } // Important to url-encode the file information as it is being stored in an @@ -128,7 +164,7 @@ info.type = info.type || "media"; element.attr('data-file_info', encodeURI(JSON.stringify(info))); - // Adding media-element class so we can find markup element later. + // Add media-element class so we can find markup element later. var classes = ['media-element']; if(info.view_mode){ @@ -224,6 +260,14 @@ }, /** + * Ensures the src tracking has been initialized and returns it. + */ + ensureSourceMap: function() { + Drupal.settings.mediaSourceMap = Drupal.settings.mediaSourceMap || {}; + return Drupal.settings.mediaSourceMap; + }, + + /** * Ensures the tag map has been initialized and returns it. */ ensure_tagmap: function () { @@ -240,4 +284,5 @@ return Drupal.settings.wysiwyg_allowed_attributes; } } + })(jQuery);