Projet

Général

Profil

Paste
Télécharger (9,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / media / modules / media_wysiwyg / js / media_wysiwyg.filter.js @ 2b3c8cc1

1
/**
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
            media.innerHTML = source.innerHTML;
50
          }
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
      // 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
      }
107

    
108
      return content;
109
    },
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
          src: element[0].src,
148
          innerHTML: element[0].innerHTML
149
        }
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
      // Add data-media-element attribute so we can find the markup element later.
162
      element.attr('data-media-element', '1')
163

    
164
      var classes = ['media-element'];
165
      if (info.view_mode) {
166
        classes.push('file-' + info.view_mode.replace(/_/g, '-'));
167
      }
168
      element.addClass(classes.join(' '));
169

    
170
      // Apply link_text if present.
171
      if (info.link_text) {
172
        $('a', element).html(info.link_text);
173
      }
174

    
175
      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
        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, '&lt;').replace(/>/g, '&gt;');
190
        }
191
        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 &quot; by \" to avoid error with JSON format.
214
              if (typeof value == 'string') {
215
                value = value.replace('&quot;', '\\"');
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);