root / drupal7 / sites / all / modules / media / modules / media_wysiwyg / js / media_wysiwyg.filter.js @ 2b3c8cc1
1 | ca0757b9 | Assos Assos | /**
|
---|---|---|---|
2 | * @file
|
||
3 | * File with utilities to handle media in html editing.
|
||
4 | */
|
||
5 | (function ($) { |
||
6 | |||
7 | Drupal.media = Drupal.media || {}; |
||
8 | /**
|
||
9 | * Utility to deal with media tokens / placeholders.
|
||
10 | */
|
||
11 | Drupal.media.filter = { |
||
12 | /**
|
||
13 | * Replaces media tokens with the placeholders for html editing.
|
||
14 | * @param content
|
||
15 | */
|
||
16 | replaceTokenWithPlaceholder: function(content) { |
||
17 | Drupal.media.filter.ensure_tagmap(); |
||
18 | var matches = content.match(/\[\[.*?\]\]/g); |
||
19 | |||
20 | if (matches) {
|
||
21 | for (var i = 0; i < matches.length; i++) { |
||
22 | var match = matches[i];
|
||
23 | if (match.indexOf('"type":"media"') == -1) { |
||
24 | continue;
|
||
25 | } |
||
26 | |||
27 | // Check if the macro exists in the tagmap. This ensures backwards
|
||
28 | // compatibility with existing media and is moderately more efficient
|
||
29 | // than re-building the element.
|
||
30 | var media = Drupal.settings.tagmap[match];
|
||
31 | var media_json = match.replace('[[', '').replace(']]', ''); |
||
32 | |||
33 | // Ensure that the media JSON is valid.
|
||
34 | try {
|
||
35 | var media_definition = JSON.parse(media_json);
|
||
36 | } |
||
37 | catch (err) {
|
||
38 | // @todo: error logging.
|
||
39 | // Content should be returned to prevent an empty editor.
|
||
40 | return content;
|
||
41 | } |
||
42 | |||
43 | // Re-build the media if the macro has changed from the tagmap.
|
||
44 | if (!media && media_definition.fid) {
|
||
45 | Drupal.media.filter.ensureSourceMap(); |
||
46 | var source = Drupal.settings.mediaSourceMap[media_definition.fid];
|
||
47 | media = document.createElement(source.tagName); |
||
48 | media.src = source.src; |
||
49 | 2b3c8cc1 | Assos Assos | media.innerHTML = source.innerHTML; |
50 | ca0757b9 | Assos Assos | } |
51 | |||
52 | // Apply attributes.
|
||
53 | var element = Drupal.media.filter.create_element(media, media_definition);
|
||
54 | var markup = Drupal.media.filter.outerHTML(element);
|
||
55 | |||
56 | // Use split and join to replace all instances of macro with markup.
|
||
57 | content = content.split(match).join(markup); |
||
58 | } |
||
59 | } |
||
60 | |||
61 | return content;
|
||
62 | }, |
||
63 | |||
64 | /**
|
||
65 | * Replaces media elements with tokens.
|
||
66 | *
|
||
67 | * @param content (string)
|
||
68 | * The markup within the wysiwyg instance.
|
||
69 | */
|
||
70 | replacePlaceholderWithToken: function(content) { |
||
71 | Drupal.media.filter.ensure_tagmap(); |
||
72 | |||
73 | // Rewrite the tagmap in case any of the macros have changed.
|
||
74 | Drupal.settings.tagmap = {}; |
||
75 | |||
76 | 2b3c8cc1 | Assos Assos | // Replace all media placeholders with their JSON macro representations.
|
77 | //
|
||
78 | // There are issues with using jQuery to parse the WYSIWYG content (see
|
||
79 | // http://drupal.org/node/1280758), and parsing HTML with regular
|
||
80 | // expressions is a terrible idea (see http://stackoverflow.com/a/1732454/854985)
|
||
81 | //
|
||
82 | // WYSIWYG editors act wacky with complex placeholder markup anyway, so an
|
||
83 | // image is the most reliable and most usable anyway: images can be moved by
|
||
84 | // dragging and dropping, and can be resized using interactive handles.
|
||
85 | //
|
||
86 | // Media requests a WYSIWYG place holder rendering of the file by passing
|
||
87 | // the wysiwyg => 1 flag in the settings array when calling
|
||
88 | // media_get_file_without_label().
|
||
89 | //
|
||
90 | // Finds the media-element class.
|
||
91 | var classRegex = 'class=[\'"][^\'"]*?media-element'; |
||
92 | // Image tag with the media-element class.
|
||
93 | var regex = '<img[^>]+' + classRegex + '[^>]*?>'; |
||
94 | // Or a span with the media-element class (used for documents).
|
||
95 | // \S\s catches any character, including a linebreak; JavaScript does not
|
||
96 | // have a dotall flag.
|
||
97 | regex += '|<span[^>]+' + classRegex + '[^>]*?>[\\S\\s]+?</span>'; |
||
98 | var matches = content.match(RegExp(regex, 'gi')); |
||
99 | if (matches) {
|
||
100 | for (i = 0; i < matches.length; i++) { |
||
101 | markup = matches[i]; |
||
102 | macro = Drupal.media.filter.create_macro($(markup));
|
||
103 | Drupal.settings.tagmap[macro] = markup; |
||
104 | content = content.replace(markup, macro); |
||
105 | } |
||
106 | ca0757b9 | Assos Assos | } |
107 | |||
108 | 2b3c8cc1 | Assos Assos | return content;
|
109 | ca0757b9 | Assos Assos | }, |
110 | |||
111 | /**
|
||
112 | * Serializes file information as a url-encoded JSON object and stores it
|
||
113 | * as a data attribute on the html element.
|
||
114 | *
|
||
115 | * @param html (string)
|
||
116 | * A html element to be used to represent the inserted media element.
|
||
117 | * @param info (object)
|
||
118 | * A object containing the media file information (fid, view_mode, etc).
|
||
119 | */
|
||
120 | create_element: function (html, info) { |
||
121 | if ($('<div>').append(html).text().length === html.length) { |
||
122 | // Element is not an html tag. Surround it in a span element so we can
|
||
123 | // pass the file attributes.
|
||
124 | html = '<span>' + html + '</span>'; |
||
125 | } |
||
126 | var element = $(html); |
||
127 | |||
128 | // Parse out link wrappers. They will be re-applied when the image is
|
||
129 | // rendered on the front-end.
|
||
130 | if (element.is('a')) { |
||
131 | element = element.children(); |
||
132 | } |
||
133 | |||
134 | // Move attributes from the file info array to the placeholder element.
|
||
135 | if (info.attributes) {
|
||
136 | $.each(Drupal.settings.media.wysiwyg_allowed_attributes, function(i, a) { |
||
137 | if (info.attributes[a]) {
|
||
138 | element.attr(a, info.attributes[a]); |
||
139 | } |
||
140 | }); |
||
141 | delete(info.attributes);
|
||
142 | |||
143 | // Store information to rebuild the element later, if necessary.
|
||
144 | Drupal.media.filter.ensureSourceMap(); |
||
145 | Drupal.settings.mediaSourceMap[info.fid] = { |
||
146 | tagName: element[0].tagName, |
||
147 | 2b3c8cc1 | Assos Assos | src: element[0].src, |
148 | innerHTML: element[0].innerHTML |
||
149 | ca0757b9 | Assos Assos | } |
150 | } |
||
151 | |||
152 | info.type = info.type || "media";
|
||
153 | |||
154 | // Store the data in the data map.
|
||
155 | Drupal.media.filter.ensureDataMap(); |
||
156 | Drupal.settings.mediaDataMap[info.fid] = info; |
||
157 | |||
158 | // Store the fid in the DOM to retrieve the data from the info map.
|
||
159 | element.attr('data-fid', info.fid);
|
||
160 | |||
161 | 2b3c8cc1 | Assos Assos | // Add data-media-element attribute so we can find the markup element later.
|
162 | element.attr('data-media-element', '1') |
||
163 | ca0757b9 | Assos Assos | |
164 | 2b3c8cc1 | Assos Assos | var classes = ['media-element']; |
165 | ca0757b9 | Assos Assos | if (info.view_mode) {
|
166 | classes.push('file-' + info.view_mode.replace(/_/g, '-')); |
||
167 | } |
||
168 | element.addClass(classes.join(' '));
|
||
169 | |||
170 | 2b3c8cc1 | Assos Assos | // Apply link_text if present.
|
171 | if (info.link_text) {
|
||
172 | $('a', element).html(info.link_text); |
||
173 | } |
||
174 | |||
175 | ca0757b9 | Assos Assos | return element;
|
176 | }, |
||
177 | |||
178 | /**
|
||
179 | * Create a macro representation of the inserted media element.
|
||
180 | *
|
||
181 | * @param element (jQuery object)
|
||
182 | * A media element with attached serialized file info.
|
||
183 | */
|
||
184 | create_macro: function (element) { |
||
185 | var file_info = Drupal.media.filter.extract_file_info(element);
|
||
186 | if (file_info) {
|
||
187 | 2b3c8cc1 | Assos Assos | if (typeof file_info.link_text == 'string') { |
188 | // Make sure the link_text-html-tags are properly escaped.
|
||
189 | file_info.link_text = file_info.link_text.replace(/</g, '<').replace(/>/g, '>'); |
||
190 | } |
||
191 | ca0757b9 | Assos Assos | return '[[' + JSON.stringify(file_info) + ']]'; |
192 | } |
||
193 | return false; |
||
194 | }, |
||
195 | |||
196 | /**
|
||
197 | * Extract the file info from a WYSIWYG placeholder element as JSON.
|
||
198 | *
|
||
199 | * @param element (jQuery object)
|
||
200 | * A media element with associated file info via a file id (fid).
|
||
201 | */
|
||
202 | extract_file_info: function (element) { |
||
203 | var fid, file_info, value;
|
||
204 | |||
205 | if (fid = element.data('fid')) { |
||
206 | Drupal.media.filter.ensureDataMap(); |
||
207 | |||
208 | if (file_info = Drupal.settings.mediaDataMap[fid]) {
|
||
209 | file_info.attributes = {}; |
||
210 | |||
211 | $.each(Drupal.settings.media.wysiwyg_allowed_attributes, function(i, a) { |
||
212 | if (value = element.attr(a)) {
|
||
213 | // Replace " by \" to avoid error with JSON format.
|
||
214 | if (typeof value == 'string') { |
||
215 | value = value.replace('"', '\\"'); |
||
216 | } |
||
217 | file_info.attributes[a] = value; |
||
218 | } |
||
219 | }); |
||
220 | |||
221 | // Extract the link text, if there is any.
|
||
222 | file_info.link_text = element.find('a').html();
|
||
223 | } |
||
224 | } |
||
225 | |||
226 | return file_info;
|
||
227 | }, |
||
228 | |||
229 | /**
|
||
230 | * Gets the HTML content of an element.
|
||
231 | *
|
||
232 | * @param element (jQuery object)
|
||
233 | */
|
||
234 | outerHTML: function (element) { |
||
235 | return element[0].outerHTML || $('<div>').append(element.eq(0).clone()).html(); |
||
236 | }, |
||
237 | |||
238 | /**
|
||
239 | * Gets the wrapped HTML content of an element to insert into the wysiwyg.
|
||
240 | *
|
||
241 | * It also registers the element in the tag map so that the token
|
||
242 | * replacement works.
|
||
243 | *
|
||
244 | * @param element (jQuery object) The element to insert.
|
||
245 | *
|
||
246 | * @see Drupal.media.filter.replacePlaceholderWithToken()
|
||
247 | */
|
||
248 | getWysiwygHTML: function (element) { |
||
249 | // Create the markup and the macro.
|
||
250 | var markup = Drupal.media.filter.outerHTML(element),
|
||
251 | macro = Drupal.media.filter.create_macro(element); |
||
252 | |||
253 | // Store macro/markup in the tagmap.
|
||
254 | Drupal.media.filter.ensure_tagmap(); |
||
255 | Drupal.settings.tagmap[macro] = markup; |
||
256 | |||
257 | // Return the html code to insert in an editor and use it with
|
||
258 | // replacePlaceholderWithToken()
|
||
259 | return markup;
|
||
260 | }, |
||
261 | |||
262 | /**
|
||
263 | * Ensures the src tracking has been initialized and returns it.
|
||
264 | */
|
||
265 | ensureSourceMap: function() { |
||
266 | Drupal.settings.mediaSourceMap = Drupal.settings.mediaSourceMap || {}; |
||
267 | return Drupal.settings.mediaSourceMap;
|
||
268 | }, |
||
269 | |||
270 | /**
|
||
271 | * Ensures the data tracking has been initialized and returns it.
|
||
272 | */
|
||
273 | ensureDataMap: function() { |
||
274 | Drupal.settings.mediaDataMap = Drupal.settings.mediaDataMap || {}; |
||
275 | return Drupal.settings.mediaDataMap;
|
||
276 | }, |
||
277 | |||
278 | /**
|
||
279 | * Ensures the tag map has been initialized and returns it.
|
||
280 | */
|
||
281 | ensure_tagmap: function () { |
||
282 | Drupal.settings.tagmap = Drupal.settings.tagmap || {}; |
||
283 | return Drupal.settings.tagmap;
|
||
284 | } |
||
285 | } |
||
286 | |||
287 | })(jQuery); |