Projet

Général

Profil

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

root / drupal7 / sites / all / modules / media_ckeditor / js / plugins / media / library.js @ 8fa03d70

1

    
2
/**
3
 *  @file
4
 *  Attach Media ckeditor behaviors.
5
 */
6

    
7
(function ($) {
8
  Drupal.media = Drupal.media || {};
9

    
10
  /**
11
   * Attaches 'insert' button to media widget.
12
   */
13
  Drupal.behaviors.mediaWidgetInsert = {
14
    attach: function (context, settings) {
15
      if (Drupal.ckeditorInstance && Drupal.settings.media_ckeditor && Drupal.settings.media_ckeditor.wysiwyg_insert) {
16
        // Only add buttons on fields that have been configured so, by
17
        // consulting the Drupal.settings.media_ckeditor.wysiwyg_insert var.
18
        for (var fieldName in Drupal.settings.media_ckeditor.wysiwyg_insert) {
19
          var fieldId = '#edit-' + fieldName.replace(/_/g, '-');
20
          // Within the field markup, look for the table of files.
21
          $(fieldId, context).find('.media-widget').once('mediaInsertButton', function() {
22
            // For each file, check to see if there is a file ID.
23
            if ($(this).find('input.fid').val() != 0) {
24
              // Now we add the button next to "Remove".
25
              var insertButton = $('<a class="media-insert button">' + Drupal.t('Insert') + '</a>')
26
                .click(function(e) {
27
                  e.preventDefault();
28
                  var fid = $(this).parent().parent().find('.fid').val();
29
                  var mediaFile = {fid: fid}
30
                  Drupal.ckeditorInstance.mediaInsert = {mediaFiles: [mediaFile]};
31
                  Drupal.ckeditorInstance.execCommand('media');
32
                });
33
              // Insert the button, differently for single vs. multi value.
34
              var multiValue = $(fieldId + ' table', context).length;
35
              if (multiValue) {
36
                insertButton.insertBefore($(this).parent().parent().find('input.remove'));
37
              }
38
              else {
39
                insertButton.insertBefore($(this).find('input.remove'));
40
              }
41
            }
42
          });
43
        }
44
      }
45
    }
46
  };
47

    
48
  Drupal.settings.ckeditor.plugins['media'] = {
49
    /**
50
     * Execute the button.
51
     */
52
    invoke: function (data, settings, instanceId) {
53
      if (data.format == 'html') {
54
        // CKEDITOR module support doesn't set this setting
55
        if (typeof settings['global'] === 'undefined') {
56
          settings['global'] = {id: 'media_wysiwyg'};
57
        }
58
        // If the selection is (or contains) an element with the attribute of
59
        // "data-media-element", assume the user wants to edit that thing.
60
        var $alreadyInsertedMedia;
61
        if (jQuery(data.node).is('[data-media-element]')) {
62
          $alreadyInsertedMedia = jQuery(data.node);
63
        }
64
        else {
65
          $alreadyInsertedMedia = jQuery(data.node).find('[data-media-element]');
66
          // For some reason, in MS Edge, data.node is a descendant of what
67
          // we are looking for, so we should also look "upwards" for media.
68
          if (!$alreadyInsertedMedia.length) {
69
            $alreadyInsertedMedia = jQuery(data.node).closest('[data-media-element]');
70
          }
71
        }
72
        // First check to see if we are using an Insert button.
73
        if (typeof Drupal.ckeditorInstance.mediaInsert !== 'undefined') {
74
          var mediaFile = Drupal.ckeditorInstance.mediaInsert.mediaFiles[0];
75
          delete Drupal.ckeditorInstance.mediaInsert;
76
          Drupal.media.popups.mediaStyleSelector(mediaFile, function (mediaFiles) {
77
            Drupal.settings.ckeditor.plugins['media'].insertMediaFile(mediaFile, mediaFiles, CKEDITOR.instances[instanceId]);
78
          }, settings['global']);
79
        }
80
        // Next check to see if we are editing already-inserted media.
81
        else if ($alreadyInsertedMedia.length) {
82
          var mediaFile = Drupal.media.filter.extract_file_info($alreadyInsertedMedia);
83
          Drupal.media.popups.mediaStyleSelector(mediaFile, function (mediaFiles) {
84
            Drupal.settings.ckeditor.plugins['media'].insertMediaFile(mediaFile, mediaFiles, CKEDITOR.instances[instanceId]);
85
          }, settings['global']);
86
        }
87
        // Otherwise we are embedding new media.
88
        else {
89
          Drupal.media.popups.mediaBrowser(function (mediaFiles) {
90
            Drupal.settings.ckeditor.plugins['media'].mediaBrowserOnSelect(mediaFiles, instanceId);
91
          }, settings['global']);
92
        }
93
      }
94
    },
95

    
96
    /**
97
     * Respond to the mediaBrowser's onSelect event.
98
     */
99
    mediaBrowserOnSelect: function (mediaFiles, instanceId) {
100
      var mediaFile = mediaFiles[0];
101
      var options = {};
102
      Drupal.media.popups.mediaStyleSelector(mediaFile, function (formattedMedia) {
103
        Drupal.settings.ckeditor.plugins['media'].insertMediaFile(mediaFile, formattedMedia, CKEDITOR.instances[instanceId]);
104
      }, options);
105

    
106
      return;
107
    },
108

    
109
    insertMediaFile: function (mediaFile, formattedMedia, ckeditorInstance, fullyRenderedFile) {
110

    
111
      // See if we should use ajax to get the fully rendered file.
112
      if (typeof fullyRenderedFile === 'undefined' &&
113
          Drupal.settings.media_ckeditor.fully_rendered_files) {
114

    
115
        $.ajax({
116
          url: Drupal.settings.basePath + 'media/rendered-in-wysiwyg',
117
          type: 'GET',
118
          data: {
119
            fid: mediaFile.fid,
120
            view_mode: formattedMedia.type,
121
            fields: formattedMedia.options
122
          },
123
          success: function(html) {
124
            // To work around an IE issue, preload any image. The issue is
125
            // that IE requests the image 2 times, and one request gets a 503,
126
            // causing a broken image icon in the WYSIWYG instead of the actual
127
            // image.
128
            var $images = $(html).find('img');
129
            if (!$images.length || $(html).find('picture').length) {
130
              // If there are no images, just insert the html immediately, by
131
              // re-calling this function with the ajax-retrieved HTML.
132
              Drupal.settings.ckeditor.plugins['media'].insertMediaFile(mediaFile, formattedMedia, ckeditorInstance, html);
133
            }
134
            else {
135
              // Otherwise do the same, but only after the first image preloads.
136
              // Possible future improvement might be to handle multiple images
137
              // instead of just the first, but even better would be to remove
138
              // this workaround entirely, when/if IE's behavior changes.
139
              var image = new Image();
140
              image.onload = function() {
141
                Drupal.settings.ckeditor.plugins['media'].insertMediaFile(mediaFile, formattedMedia, ckeditorInstance, html);
142
              }
143
              image.src = $images.first().attr('src');
144
            }
145
          },
146
          error: function(data) {
147
            // Fallback to whatever the HTML was already going to be.
148
            Drupal.settings.ckeditor.plugins['media'].insertMediaFile(mediaFile, formattedMedia, ckeditorInstance, formattedMedia.html);
149
          }
150
        });
151

    
152
        // Stop for now, because the callback above re-calls this same function.
153
        return;
154
      }
155

    
156
      // See if we already used ajax to get the fully rendered file.
157
      if (typeof fullyRenderedFile !== 'undefined') {
158
        formattedMedia.html = fullyRenderedFile;
159
      }
160

    
161
      // Customization of Drupal.media.filter.registerNewElement().
162
      var element = Drupal.media.filter.create_element(formattedMedia.html, {
163
        fid: mediaFile.fid,
164
        view_mode: formattedMedia.type,
165
        attributes: mediaFile.attributes,
166
        fields: formattedMedia.options
167
      });
168

    
169
      var hasWidgetSupport = typeof(CKEDITOR.plugins.registered.widget) != 'undefined';
170

    
171
      // Use own wrapper element to be able to properly deal with selections.
172
      // Check prepareDataForWysiwygMode() in plugin.js for details.
173
      var wysiwygHTML = Drupal.media.filter.getWysiwygHTML(element);
174

    
175
      if (wysiwygHTML.indexOf("<!--MEDIA-WRAPPER-START-") !== -1) {
176
        ckeditorInstance.plugins.media.mediaLegacyWrappers = true;
177
        wysiwygHTML = wysiwygHTML.replace(/<!--MEDIA-WRAPPER-START-(\d+)-->(.*?)<!--MEDIA-WRAPPER-END-\d+-->/gi, '');
178
      }
179

    
180
      // Insert element. Use CKEDITOR.dom.element.createFromHtml to ensure our
181
      // custom wrapper element is preserved.
182
      var editorElement = CKEDITOR.dom.element.createFromHtml(wysiwygHTML);
183
      ckeditorInstance.insertElement(editorElement);
184

    
185
      // Initialize widget on our html if possible.
186
      if (Drupal.settings.ckeditor.plugins['media'].compareVersions(CKEDITOR.version, '4.3') >= 0 && hasWidgetSupport) {
187
        ckeditorInstance.widgets.initOn( editorElement, 'mediabox' );
188

    
189
        // Also support the image2 plugin.
190
        ckeditorInstance.widgets.initOn( editorElement, 'image' );
191
      }
192
    },
193

    
194
    /**
195
     * Compare versions of CKEditor to determine if one version
196
     * is less than, greater than, or equal to another.
197
     *
198
     * @param a
199
     *   A first instance of CKEditor.
200
     * @param b
201
     *   A second instance of CKEditor.
202
     * @return
203
     *   A number less than, greater than, or equal to zero.
204
     *   - If a < b, a number less than zero is returned.
205
     *   - If a > b, a number greater than zero is return.
206
     *   - If a = b, the value of 0 is returned.
207
     */
208
    compareVersions: function (a, b) {
209
      var i, diff;
210
      var regExStrip0 = /(\.0+)+$/;
211
      var segmentsA = a.replace(regExStrip0, '').split('.');
212
      var segmentsB = b.replace(regExStrip0, '').split('.');
213
      var l = Math.min(segmentsA.length, segmentsB.length);
214

    
215
      for (i = 0; i < l; i++) {
216
        diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
217
        if (diff) {
218
          return diff;
219
        }
220
      }
221

    
222
      return segmentsA.length - segmentsB.length;
223
    },
224

    
225
    /**
226
     * Forces custom attributes into the class field of the specified image.
227
     *
228
     * Due to a bug in some versions of Firefox
229
     * (http://forums.mozillazine.org/viewtopic.php?f=9&t=1991855), the
230
     * custom attributes used to share information about the image are
231
     * being stripped as the image markup is set into the rich text
232
     * editor.  Here we encode these attributes into the class field so
233
     * the data survives.
234
     *
235
     * @param imgElement
236
     *   The image
237
     * @fid
238
     *   The file id.
239
     * @param view_mode
240
     *   The view mode.
241
     * @param additional
242
     *   Additional attributes to add to the image.
243
     */
244
    forceAttributesIntoClass: function (imgElement, fid, view_mode, additional) {
245
      var wysiwyg = imgElement.attr('wysiwyg');
246
      if (wysiwyg) {
247
        imgElement.addClass('attr__wysiwyg__' + wysiwyg);
248
      }
249
      var format = imgElement.attr('format');
250
      if (format) {
251
        imgElement.addClass('attr__format__' + format);
252
      }
253
      var typeOf = imgElement.attr('typeof');
254
      if (typeOf) {
255
        imgElement.addClass('attr__typeof__' + typeOf);
256
      }
257
      if (fid) {
258
        imgElement.addClass('img__fid__' + fid);
259
      }
260
      if (view_mode) {
261
        imgElement.addClass('img__view_mode__' + view_mode);
262
      }
263
      if (additional) {
264
        for (var name in additional) {
265
          if (additional.hasOwnProperty(name)) {
266
            switch (name) {
267
              case 'field_file_image_alt_text[und][0][value]':
268
                imgElement.attr('alt', additional[name]);
269
                break;
270
              case 'field_file_image_title_text[und][0][value]':
271
                imgElement.attr('title', additional[name]);
272
                break;
273
              default:
274
                imgElement.addClass('attr__' + name + '__' + additional[name]);
275
                break;
276
            }
277
          }
278
        }
279
      }
280
    },
281

    
282
    /**
283
     * Retrieves encoded attributes from the specified class string.
284
     *
285
     * @param classString
286
     *   A string containing the value of the class attribute.
287
     * @return
288
     *   An array containing the attribute names as keys, and an object
289
     *   with the name, value, and attribute type (either 'attr' or
290
     *   'img', depending on whether it is an image attribute or should
291
     *   be it the attributes section)
292
     */
293
    getAttributesFromClass: function (classString) {
294
      var actualClasses = [];
295
      var otherAttributes = [];
296
      var classes = classString.split(' ');
297
      var regexp = new RegExp('^(attr|img)__([^\S]*)__([^\S]*)$');
298
      for (var index = 0; index < classes.length; index++) {
299
        var matches = classes[index].match(regexp);
300
        if (matches && matches.length === 4) {
301
          otherAttributes[matches[2]] = {
302
            name: matches[2],
303
            value: matches[3],
304
            type: matches[1]
305
          };
306
        }
307
        else {
308
          actualClasses.push(classes[index]);
309
        }
310
      }
311
      if (actualClasses.length > 0) {
312
        otherAttributes['class'] = {
313
          name: 'class',
314
          value: actualClasses.join(' '),
315
          type: 'attr'
316
        };
317
      }
318
      return otherAttributes;
319
    },
320

    
321
    sortAttributes: function (a, b) {
322
      var nameA = a.name.toLowerCase();
323
      var nameB = b.name.toLowerCase();
324
      if (nameA < nameB) {
325
        return -1;
326
      }
327
      if (nameA > nameB) {
328
        return 1;
329
      }
330
      return 0;
331
    }
332
  };
333

    
334
  // If media_ckeditor is configured to render items in the wysiwyg as full
335
  // rendered file entities, we need to completely hijack a function from
336
  // media_wysiwyg.filter.js.
337
  if (Drupal.settings.media_ckeditor.fully_rendered_files) {
338

    
339
    // Replaces function of the same name, from media_wysiwyg.filter.js.
340
    Drupal.media.filter.replacePlaceholderWithToken = function(content) {
341

    
342
      var $placeholder = $(content);
343
      if ($placeholder.hasClass('media-element')) {
344
        var macro = Drupal.media.filter.create_macro($placeholder);
345
        Drupal.media.filter.ensure_tagmap();
346
        Drupal.settings.tagmap[macro] = content;
347
        return macro;
348
      }
349
      return content;
350
    }
351
  }
352
})(jQuery);