PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents / 4.6.2
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents v4.6.2
4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 2.0.0 2.0.1 2.0.2 2.0.3 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.0 2.2.1 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.1.3 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9 4.0.0 4.0.1 4.0.10 4.0.11 4.0.12 4.0.13 4.0.14 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.0.9 4.1.0 4.1.1 4.1.10 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.7 4.1.8 4.1.9 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.8 4.2.9 4.3.0 4.3.1 4.4.0 4.4.1 4.4.10 4.4.11 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 4.5.0 4.5.1
embedpress / assets / js / preview.js
embedpress / assets / js Last commit date
chunks 5 days ago vendor 9 months ago admin.build.js 1 month ago admin.js 3 months ago analytics-tracker.js 1 month ago analytics.build.js 5 days ago blocks.build.js 5 days ago carousel.js 1 year ago custom-player.build.js 1 month ago documents-viewer-script.js 3 months ago ep-gr-elementor-control.js 3 weeks ago ep-pdf-lightbox.js 3 months ago ep-view-count.js 1 month ago ep-yt-queue.js 1 month ago feature-notices.js 8 months ago feature-preview-modal.js 3 weeks ago front.js 3 weeks ago frontend.build.js 3 months ago gallery-justify.js 1 month ago google-reviews.build.js 3 weeks ago google-reviews.js 3 weeks ago gutneberg-script.js 2 months ago index.html 7 years ago initCarousel.js 2 years ago initplyr.js 2 months ago instafeed.js 1 month ago instagram-shortcode-generator.js 2 months ago lazy-load.js 7 months ago license.js 4 months ago meetup-timezone.js 3 months ago onboarding.build.js 1 month ago pdf-gallery-elementor-editor.js 3 months ago pdf-gallery.js 3 months ago preview.js 1 week ago settings.js 3 months ago sponsored.js 10 months ago
preview.js
1658 lines
1 /**
2 * @package EmbedPress
3 * @author EmbedPress <help@embedpress.com>
4 * @copyright Copyright (C) 2018 EmbedPress. All rights reserved.
5 * @license GPLv2 or later
6 * @since 1.0
7 */
8 (function ($, String, embedpressPreviewData, undefined) {
9 'use strict';
10
11 $(window.document).ready(function () {
12 String.prototype.capitalizeFirstLetter = function () {
13 return this.charAt(0).toUpperCase() + this.slice(1);
14 };
15
16 String.prototype.isValidUrl = function () {
17 var rule = /^(https?|embedpresss?):\/\//i;
18
19 return rule.test(this.toString());
20 };
21
22 String.prototype.hasShortcode = function (shortcode) {
23 var shortcodeRule = new RegExp('\\[' + shortcode + '(?:\\]|.+?\\])', 'ig');
24 return !!this.toString().match(shortcodeRule);
25 };
26
27 String.prototype.stripShortcode = function (shortcode) {
28 var stripRule = new RegExp('(\\[' + shortcode + '(?:\\]|.+?\\])|\\[\\/' + shortcode + '\\])', 'ig');
29 return this.toString().replace(stripRule, '');
30 };
31
32 String.prototype.setShortcodeAttribute = function (attr, value, shortcode, replaceInsteadOfMerge) {
33 replaceInsteadOfMerge = typeof replaceInsteadOfMerge === 'undefined' ? false : replaceInsteadOfMerge;
34 var subject = this.toString();
35
36 if (subject.hasShortcode(shortcode)) {
37 var attributes = subject.getShortcodeAttributes(shortcode);
38
39 if (attributes.hasOwnProperty(attr)) {
40 if (replaceInsteadOfMerge) {
41 attributes[attr] = value;
42 } else {
43 attributes[attr] += ' ' + value;
44 }
45 } else {
46 attributes[attr] = value;
47 }
48
49 if (!!Object.keys(attributes).length) {
50 var parsedAttributes = [];
51 for (var ats in attributes) {
52 parsedAttributes.push(ats + '="' + attributes[ats] + '"');
53 }
54
55 subject = '[' + shortcode + ' ' + parsedAttributes.join(' ') + ']' + subject.stripShortcode(shortcode) + '[/' + shortcode + ']';
56 } else {
57 subject = '[' + shortcode + ']' + subject.stripShortcode(shortcode) + '[/' + shortcode + ']';
58 }
59
60 return subject;
61 } else {
62 return subject;
63 }
64 };
65
66 String.prototype.getShortcodeAttributes = function (shortcode) {
67 var subject = this.toString();
68 if (subject.hasShortcode(shortcode)) {
69 var attributes = {};
70 var propertiesString = (new RegExp(/\[embed\s*(.*?)\]/ig)).exec(subject)[1]; // Separate all shortcode attributes from the rest of the string
71 if (propertiesString.length > 0) {
72 var extractAttributesRule = new RegExp(/(\!?\w+-?\w*)(?:="(.+?)")?/ig); // Extract attributes and their values
73 var match;
74 while (match = extractAttributesRule.exec(propertiesString)) {
75 var attrName = match[1];
76 var attrValue;
77 if (match[2] === undefined) {
78 // Prevent `class` property being empty an treated as a boolean param
79 if (attrName.toLowerCase() !== 'class') {
80 if (attrName.indexOf('!') === 0) {
81 attrName = attrName.replace('!', '');
82 attrValue = 'false';
83 } else {
84 attrValue = 'true';
85 }
86
87 attributes[attrName] = attrValue;
88 }
89 } else {
90 attrValue = match[2];
91 if (attrValue.isBoolean()) {
92 attrValue = attrValue.isFalse() ? 'false' : 'true';
93 }
94
95 attributes[attrName] = attrValue;
96 }
97 }
98 match = extractAttributesRule = null;
99 }
100 propertiesString = null;
101
102 return attributes;
103 } else {
104 return {};
105 }
106 };
107
108 String.prototype.isBoolean = function () {
109 var subject = this.toString().trim().toLowerCase();
110
111 return subject.isTrue(false) || subject.isFalse();
112 };
113
114 String.prototype.isTrue = function (defaultValue) {
115 var subject = this.toString().trim().toLowerCase();
116 defaultValue = typeof defaultValue === undefined ? true : defaultValue;
117
118 switch (subject) {
119 case '':
120 defaultValue += '';
121 return !defaultValue.isFalse();
122 case '1':
123 case 'true':
124 case 'on':
125 case 'yes':
126 case 'y':
127 return true;
128 default:
129 return false;
130 }
131 };
132
133 String.prototype.isFalse = function () {
134 var subject = this.toString().trim().toLowerCase();
135
136 switch (subject) {
137 case '0':
138 case 'false':
139 case 'off':
140 case 'no':
141 case 'n':
142 case 'nil':
143 case 'null':
144 return true;
145 default:
146 return false;
147 }
148 };
149
150 var SHORTCODE_REGEXP = new RegExp('\\[\/?' + embedpressPreviewData.shortcode + '\\]', 'gi');
151
152 var EmbedPress = function () {
153 var self = this;
154
155 var PLG_SYSTEM_ASSETS_CSS_PATH = embedpressPreviewData.assetsUrl + 'css';
156 var PLG_CONTENT_ASSETS_CSS_PATH = PLG_SYSTEM_ASSETS_CSS_PATH;
157
158 /**
159 * The default params
160 *
161 * @type Object
162 */
163 self.params = {
164 baseUrl: '',
165 versionUID: '0'
166 };
167
168 /**
169 * True, if user agent is iOS
170 * @type Boolean True, if is iOS
171 */
172 self.iOS = /iPad|iPod|iPhone/.test(window.navigator.userAgent);
173
174 /**
175 * The active wrapper, activated by the mouse enter event
176 * @type Element
177 */
178 self.activeWrapper = null;
179
180 self.activeWrapperForModal = null;
181
182 /**
183 * The active controller panel
184 * @type Element
185 */
186 self.activeControllerPanel = null;
187
188 /**
189 * A list containing all loaded editor instances on the page
190 * @type Array
191 */
192 self.loadedEditors = [];
193
194 /**
195 * Init the plugin
196 *
197 * @param object params Override the plugin's params
198 * @return void
199 */
200 self.init = function (params) {
201 $.extend(self.params, params);
202
203 // Fix iOS doesn't firing click events on 'standard' elements
204 if (self.iOS) {
205 $(window.document.body).css('cursor', 'pointer');
206 }
207
208 $(self.onReady);
209 };
210
211 self.addEvent = function (event, element, callback) {
212 if (typeof element.on !== 'undefined') {
213 element.on(event, callback);
214 } else {
215 if (element['on' + event.capitalizeFirstLetter()]) {
216 element['on' + event.capitalizeFirstLetter()].add(callback);
217 }
218 }
219 };
220
221 self.isEmpty = function (list) {
222 return list.length === 0;
223 };
224
225 self.isDefined = function (attribute) {
226 return (typeof attribute !== 'undefined') && (attribute !== null);
227 };
228
229 self.makeId = function () {
230 var text = '';
231 var possible = 'abcdefghijklmnopqrstuvwxyz0123456789';
232
233 for (var i = 0; i < 5; i++)
234 text += possible.charAt(Math.floor(Math.random() * possible.length));
235
236 return text;
237 };
238
239 self.loadAsyncDynamicJsCodeFromElement = function (subject, wrapper, editorInstance) {
240 subject = $(subject);
241 if (subject.prop('tagName') && subject.prop('tagName').toLowerCase() === 'script') {
242 var scriptSrc = subject.attr('src') || null;
243 if (!scriptSrc) {
244 self.addScriptDeclaration(wrapper, subject.html(), editorInstance);
245 } else {
246 self.addScript(scriptSrc, null, wrapper, editorInstance);
247 }
248 } else {
249 var innerScriptsList = $('script', subject);
250 if (innerScriptsList.length > 0) {
251 $.each(innerScriptsList, function (innerScriptIndex, innerScript) {
252 self.loadAsyncDynamicJsCodeFromElement(innerScript, wrapper, editorInstance);
253 });
254 }
255 }
256 };
257
258 /**
259 * Method executed on the document ready event
260 *
261 * @return void
262 */
263 self.onReady = function () {
264 var findEditors = function () {
265 // Wait until the editor is available
266 var interval = window.setInterval(
267 function () {
268 var editorsFound = self.getEditors();
269 if (editorsFound.length) {
270 self.loadedEditors = editorsFound;
271
272 for (var editorIndex = 0; editorIndex < self.loadedEditors.length; editorIndex++) {
273 self.onFindEditor(self.loadedEditors[editorIndex]);
274 }
275
276 window.clearInterval(interval);
277
278 return self.loadedEditors;
279 }
280 },
281 250
282 );
283 };
284
285 if (self.tinymceIsAvailable()) {
286 findEditors();
287 }
288
289 // Add support for the Beaver Builder.
290 if (typeof FLLightbox !== 'undefined') {
291 $.each(FLLightbox._instances, function (index) {
292 FLLightbox._instances[index].on('open', function () {
293 setTimeout(function () {
294 findEditors();
295 }, 500);
296 });
297
298 FLLightbox._instances[index].on('didHideLightbox', function () {
299 setTimeout(function () {
300 findEditors();
301 }, 500);
302 });
303 });
304 }
305 };
306
307 /**
308 * Detects if tinymce object is available
309 * @return Boolean True, if available
310 */
311 self.tinymceIsAvailable = function () {
312 return typeof window.tinymce === 'object' || typeof window.tinyMCE === 'object';
313 };
314
315 /**
316 * Returns true if the controller panel is active
317 * @return Boolean True, if the controller panel is active
318 */
319 self.controllerPanelIsActive = function () {
320 return typeof self.activeControllerPanel !== 'undefined' && self.activeControllerPanel !== null;
321 };
322
323 /**
324 * Returns the editor
325 * @return Object The editor
326 */
327 self.getEditors = function () {
328 if (!window.tinymce || !window.tinymce.editors || window.tinymce.editors.length === 0) {
329 return [];
330 }
331
332 return window.tinymce.editors || [];
333 };
334
335 /**
336 * Parses the content, sending it to the component which will
337 * look for urls to be parsed into embed codes
338 *
339 * @param string content The content
340 * @param function onsuccess The callback called on success
341 * @return void
342 */
343 self.getParsedContent = function (content, onsuccess) {
344 // Get the parsed content
345 $.ajax({
346 type: 'POST',
347 url: self.params.baseUrl + 'wp-admin/admin-ajax.php',
348 data: {
349 action: 'embedpress_do_ajax_request',
350 subject: content,
351 _ajax_nonce: self.params.nonce || ''
352 },
353 success: onsuccess,
354 dataType: 'json',
355 async: true
356 });
357 };
358
359 self.addStylesheet = function (url, editorInstance) {
360 var head = editorInstance.getDoc().getElementsByTagName('head')[0];
361
362 var $style = $('<link rel="stylesheet" type="text/css" href="' + url + '">');
363 $style.appendTo(head);
364 };
365
366 self.convertURLSchemeToPattern = function (scheme) {
367 var prefix = '(.*)((?:http|embedpress)s?:\\/\\/(?:www\\.)?',
368 suffix = '[\\/]?)(.*)',
369 pattern;
370
371 scheme = scheme.replace(/\*/g, '[a-zA-Z0-9=&_\\-\\?\\.\\/!\\+%:@,#]+');
372 scheme = scheme.replace(/\./g, '\\.');
373 scheme = scheme.replace(/\//g, '\\/');
374
375 return prefix + scheme + suffix;
376 };
377
378 self.getProvidersURLPatterns = function () {
379 // @todo: Add option to disable/enable the providers
380 var patterns = [];
381
382 self.each(embedpressPreviewData.urlSchemes, function convertEachURLSchemesToPattern (scheme) {
383 patterns.push(self.convertURLSchemeToPattern(scheme));
384 });
385
386 return patterns;
387 };
388
389 self.addScript = function (source, callback, wrapper, editorInstance) {
390 var doc = editorInstance.getDoc();
391
392 if (typeof wrapper === 'undefined' || !wrapper) {
393 wrapper = $(doc.getElementsByTagName('head')[0]);
394 }
395
396 var $script = $(doc.createElement('script'));
397 $script.attr('async', 1);
398
399 if (typeof callback === 'function') {
400 $script.ready(callback);
401 }
402
403 $script.attr('src', source);
404
405 wrapper.append($script);
406 };
407
408 self.addScriptDeclaration = function (wrapper, declaration, editorInstance) {
409 var doc = editorInstance.getDoc(),
410 $script = $(doc.createElement('script'));
411
412 $(wrapper).append($script);
413
414 $script.text(declaration);
415 };
416
417 self.addURLsPlaceholder = function (node, url, editorInstance) {
418 var uid = self.makeId();
419
420 var wrapperClasses = ['embedpress_wrapper', 'embedpress_placeholder', 'wpview', 'wpview-wrap'];
421
422 var shortcodeAttributes = node.value.getShortcodeAttributes(embedpressPreviewData.shortcode);
423 var customAttributes = shortcodeAttributes;
424
425 var customClasses = '';
426 if (!!Object.keys(shortcodeAttributes).length) {
427 var specialAttributes = ['class', 'href', 'data-href'];
428 // Iterates over each attribute of shortcodeAttributes to add the prefix "data-" if missing
429 var dataPrefix = 'data-';
430 var prefixedShortcodeAttributes = [];
431 for (var attr in shortcodeAttributes) {
432 if (specialAttributes.indexOf(attr) === -1) {
433 if (attr.indexOf(dataPrefix) !== 0) {
434 prefixedShortcodeAttributes[dataPrefix + attr] = shortcodeAttributes[attr];
435 } else {
436 prefixedShortcodeAttributes[attr] = shortcodeAttributes[attr];
437 }
438 } else {
439 attr = attr.replace(dataPrefix, '');
440 if (attr === 'class') {
441 wrapperClasses.push(shortcodeAttributes[attr]);
442 }
443 }
444 }
445
446 shortcodeAttributes = prefixedShortcodeAttributes;
447 prefixedShortcodeAttributes = dataPrefix = null;
448 }
449
450 if (('data-width' in shortcodeAttributes || 'data-height' in shortcodeAttributes) && 'data-responsive' in shortcodeAttributes) {
451 shortcodeAttributes['data-responsive'] = 'false';
452 }
453
454 var wrapper = new self.Node('div', 1);
455 var wrapperSettings = {
456 'class': Array.from(new Set(wrapperClasses)).join(' '),
457 'data-url': url,
458 'data-uid': uid,
459 'id': 'embedpress_wrapper_' + uid,
460 'data-loading-text': 'Loading your embed...'
461 };
462
463 wrapperSettings = $.extend({}, wrapperSettings, shortcodeAttributes);
464
465 if (wrapperSettings.class.indexOf('is-loading') === -1) {
466 wrapperSettings.class += ' is-loading';
467 }
468
469 wrapper.attr(wrapperSettings);
470
471 var panel = new self.Node('div', 1);
472 panel.attr({
473 'id': 'embedpress_controller_panel_' + uid,
474 'class': 'embedpress_controller_panel embedpress_ignore_mouseout hidden'
475 });
476 wrapper.append(panel);
477
478 function createGhostNode (htmlTag, content) {
479 htmlTag = htmlTag || 'span';
480 content = content || '&nbsp;';
481
482 var ghostNode = new self.Node(htmlTag, 1);
483 ghostNode.attr({
484 'class': 'hidden'
485 });
486
487 var ghostText = new self.Node('#text', 3);
488 ghostText.value = content;
489 ghostNode.append(ghostText);
490
491 return ghostNode;
492 }
493
494 var editButton = new self.Node('div', 1);
495 editButton.attr({
496 'id': 'embedpress_button_edit_' + uid,
497 'class': 'embedpress_ignore_mouseout embedpress_controller_button'
498 });
499 var editButtonIcon = new self.Node('div', 1);
500 editButtonIcon.attr({
501 'class': 'embedpress-icon-pencil embedpress_ignore_mouseout'
502 });
503 editButtonIcon.append(createGhostNode());
504 editButton.append(editButtonIcon);
505 panel.append(editButton);
506
507 var removeButton = new self.Node('div', 1);
508 removeButton.attr({
509 'id': 'embedpress_button_remove_' + uid,
510 'class': 'embedpress_ignore_mouseout embedpress_controller_button'
511 });
512 var removeButtonIcon = new self.Node('div', 1);
513 removeButtonIcon.attr({
514 'class': 'embedpress-icon-x embedpress_ignore_mouseout'
515 });
516 removeButtonIcon.append(createGhostNode());
517 removeButton.append(removeButtonIcon);
518 panel.append(removeButton);
519
520 node.value = node.value.trim();
521
522 node.replace(wrapper);
523
524 // Trigger the timeout which will load the content
525 window.setTimeout(function () {
526 self.parseContentAsync(uid, url, customAttributes, editorInstance);
527 }, 200);
528
529 return wrapper;
530 };
531
532 self.parseContentAsync = function (uid, url, customAttributes, editorInstance) {
533 customAttributes = typeof customAttributes === 'undefined' ? {} : customAttributes;
534
535 url = self.decodeEmbedURLSpecialChars(url, true, customAttributes);
536 var rawUrl = url.stripShortcode(embedpressPreviewData.shortcode);
537
538 $(self).triggerHandler('EmbedPress.beforeEmbed', {
539 'url': rawUrl,
540 'meta': {
541 'attributes': customAttributes || {}
542 }
543 });
544
545 // Get the parsed embed code from the EmbedPress plugin
546 self.getParsedContent(url, function getParsedContentCallback (result) {
547 var embeddedContent = (typeof result.data === 'object' ? result.data.embed : result.data).stripShortcode(embedpressPreviewData.shortcode);
548 var $wrapper = $(self.getElementInContentById('embedpress_wrapper_' + uid, editorInstance));
549 var wrapperParent = $($wrapper.parent());
550
551 // Check if $wrapper was rendered inside a <p> element.
552 if (wrapperParent.prop('tagName') && wrapperParent.prop('tagName').toUpperCase() === 'P') {
553 wrapperParent.replaceWith($wrapper);
554 // Check if there's at least one "space" after $wrapper.
555 var nextSibling = $($wrapper).next();
556 if (!nextSibling.length || nextSibling.prop('tagName').toUpperCase() !== 'P') {
557 //$('<p>&nbsp;</p>').insertAfter($wrapper);
558 }
559 nextSibling = null;
560 }
561 wrapperParent = null;
562
563 // Check if the url could not be embedded for some reason.
564 if (rawUrl === embeddedContent) {
565 // Echoes the raw url
566 $wrapper.replaceWith($('<p>' + rawUrl + '</p>'));
567 return;
568 }
569
570 $wrapper.removeClass('is-loading');
571
572 // Parse as DOM element
573 var $content;
574 try {
575 $content = $(embeddedContent);
576 } catch (err) {
577 // Fallback to a div, if the result is not a html markup, e.g. a url
578 $content = $('<div>');
579 $content.html(embeddedContent);
580 }
581
582 if (!$('iframe', $content).length && result.data.provider_name!=='Infogram') {
583 var contentWrapper = $($content).clone();
584 contentWrapper.html('');
585
586 $wrapper.removeClass('embedpress_placeholder');
587
588 $wrapper.append(contentWrapper);
589
590 setTimeout(function () {
591 editorInstance.undoManager.transact(function () {
592 var iframe = editorInstance.getDoc().createElement('iframe');
593 iframe.src = tinymce.Env.ie ? 'javascript:""' : '';
594 iframe.frameBorder = '0';
595 iframe.allowTransparency = 'true';
596 iframe.scrolling = 'no';
597 iframe.class = 'wpview-sandbox';
598 iframe.style.width = '100%';
599
600 contentWrapper.append(iframe);
601
602 var iframeWindow = iframe.contentWindow;
603 // Content failed to load.
604 if (!iframeWindow) {
605 return;
606 }
607
608 var iframeDoc = iframeWindow.document;
609
610 $(iframe).load(function () {
611 var maximumChecksAllowed = 8;
612 var checkIndex = 0;
613
614 var checkerInterval = setInterval(function () {
615 if (checkIndex === maximumChecksAllowed) {
616 clearInterval(checkerInterval);
617
618 setTimeout(function () {
619 $wrapper.css('width', iframe.width);
620 $wrapper.css('height', iframe.height);
621 }, 100);
622 } else {
623 if (customAttributes.height) {
624 iframe.height = customAttributes.height;
625 iframe.style.height = customAttributes.height + 'px';
626 } else {
627 iframe.height = $('body', iframeDoc).height();
628 }
629
630 if (customAttributes.width) {
631 iframe.width = customAttributes.width;
632 iframe.style.width = customAttributes.width + 'px';
633 } else {
634 iframe.width = $('body', iframeDoc).width();
635 }
636
637 checkIndex++;
638 }
639 }, 250);
640 });
641
642 iframeDoc.open();
643 iframeDoc.write(
644 '<!DOCTYPE html>' +
645 '<html>' +
646 '<head>' +
647 '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />' +
648 '<style>' +
649 'html {' +
650 'background: transparent;' +
651 'padding: 0;' +
652 'margin: 0;' +
653 '}' +
654 'body#wpview-iframe-sandbox {' +
655 'background: transparent;' +
656 'padding: 1px 0 !important;' +
657 'margin: -1px 0 0 !important;' +
658 '}' +
659 'body#wpview-iframe-sandbox:before,' +
660 'body#wpview-iframe-sandbox:after {' +
661 'display: none;' +
662 'content: "";' +
663 '}' +
664 '</style>' +
665 '</head>' +
666 '<body id="wpview-iframe-sandbox" class="' + editorInstance.getBody().className + '" style="display: inline-block; width: 100%;" >' +
667 $content.html() +
668 '</body>' +
669 '</html>'
670 );
671 iframeDoc.close();
672 });
673 }, 50);
674 } else {
675 $wrapper.removeClass('embedpress_placeholder');
676
677 self.appendElementsIntoWrapper($content, $wrapper, editorInstance);
678 }
679
680 $wrapper.append($('<span class="wpview-end"></span>'));
681
682 if (result && result.data && typeof result.data === 'object') {
683 result.data.width = $($wrapper).width();
684 result.data.height = $($wrapper).height();
685 }
686
687 $(self).triggerHandler('EmbedPress.afterEmbed', {
688 'meta': result.data,
689 'url': rawUrl,
690 'wrapper': $wrapper
691 });
692 });
693 };
694
695 self.appendElementsIntoWrapper = function (elementsList, wrapper, editorInstance) {
696 if (elementsList.length > 0) {
697 $.each(elementsList, function appendElementIntoWrapper (elementIndex, element) {
698 // Check if the element is a script and do not add it now (if added here it wouldn't be executed)
699 if (element.tagName && element.tagName.toLowerCase() !== 'script') {
700 wrapper.append($(element));
701
702 if (element.tagName.toLowerCase() === 'iframe') {
703 $(element).ready(function () {
704 window.setTimeout(function () {
705 $.each(editorInstance.dom.select('div.embedpress_wrapper iframe'), function (elementIndex, iframe) {
706 self.fixIframeSize(iframe);
707 });
708 }, 300);
709 });
710 } else if (element.tagName.toLowerCase() === 'div') {
711 if ($('img', $(element)).length || $('blockquote', wrapper).length) {
712 // This ensures that the embed wrapper have the same width as its content
713 $($(element).parents('.embedpress_wrapper').get(0)).addClass('dynamic-width');
714 }
715
716 $(element).css('max-width', $($(element).parents('body').get(0)).width());
717 }
718 }
719
720 self.loadAsyncDynamicJsCodeFromElement(element, wrapper, editorInstance);
721 });
722 }
723
724 return wrapper;
725 };
726
727 self.encodeEmbedURLSpecialChars = function (content) {
728 if (content.match(SHORTCODE_REGEXP)) {
729 var subject = content.replace(SHORTCODE_REGEXP, '');
730
731 if (!subject.isValidUrl()) {
732 return content;
733 }
734
735 content = subject;
736 subject = null;
737 }
738
739 // Bypass the autolink plugin, avoiding to have the url converted to a link automatically
740 content = content.replace(/http(s?)\:\/\//i, 'embedpress$1://');
741
742 // Bypass the autolink plugin, avoiding to have some urls with @ being treated as email address (e.g. GMaps)
743 content = content.replace('@', '::__at__::').trim();
744
745 return content;
746 };
747
748 self.decodeEmbedURLSpecialChars = function (content, applyShortcode, attributes) {
749 var encodingRegexpRule = /embedpress(s?):\/\//;
750 applyShortcode = (typeof applyShortcode === 'undefined') ? true : applyShortcode;
751 attributes = (typeof attributes === 'undefined') ? {} : attributes;
752
753 var isEncoded = content.match(encodingRegexpRule);
754
755 // Restore http[s] in the url (converted to bypass autolink plugin)
756 content = content.replace(/embedpress(s?):\/\//, 'http$1://');
757 content = content.replace('::__at__::', '@').trim();
758
759 if ('class' in attributes) {
760 var classesList = attributes.class.split(/\s/g);
761 var shouldRemoveDynamicWidthClass = false;
762 for (var classIndex = 0; classIndex < classesList.length; classIndex++) {
763 if (classesList[classIndex] === 'dynamic-width') {
764 shouldRemoveDynamicWidthClass = classIndex;
765 break;
766 }
767 }
768
769 if (shouldRemoveDynamicWidthClass !== false) {
770 classesList.splice(shouldRemoveDynamicWidthClass, 1);
771
772 if (classesList.length === 0) {
773 delete attributes.class;
774 }
775
776 attributes.class = classesList.join(' ');
777 }
778
779 shouldRemoveDynamicWidthClass = classesList = classIndex = null;
780 }
781
782 if (isEncoded && applyShortcode) {
783 var shortcode = '[' + embedpressPreviewData.shortcode;
784 if (!!Object.keys(attributes).length) {
785 var attrValue;
786
787 for (var attrName in attributes) {
788 attrValue = attributes[attrName];
789
790 // Prevent `class` property being empty an treated as a boolean param
791 if (attrName.toLowerCase() === 'class' && !attrValue.length) {
792
793 } else {
794 if (attrValue.isBoolean()) {
795 shortcode += ' ';
796 if (attrValue.isFalse()) {
797 shortcode += '!';
798 }
799
800 shortcode += attrName;
801 } else {
802 shortcode += ' ' + attrName + '="' + attrValue + '"';
803 }
804 }
805 }
806 attrValue = attrName = null;
807 }
808
809 content = shortcode + ']' + content + '[/' + embedpressPreviewData.shortcode + ']';
810 }
811
812 return content;
813 };
814
815 /**
816 * Method executed after find the editor. It will make additional
817 * configurations and add the content's stylesheets for the preview
818 *
819 * @return void
820 */
821 self.onFindEditor = function (editorInstance) {
822 self.each = tinymce.each;
823 self.extend = tinymce.extend;
824 self.JSON = tinymce.util.JSON;
825 self.Node = tinymce.html.Node;
826
827 function onFindEditorCallback () {
828 self.addStylesheet(PLG_SYSTEM_ASSETS_CSS_PATH + '/font.css?v=' + self.params.versionUID, editorInstance, editorInstance);
829 self.addStylesheet(PLG_SYSTEM_ASSETS_CSS_PATH + '/preview.css?v=' + self.params.versionUID, editorInstance, editorInstance);
830 self.addStylesheet(PLG_CONTENT_ASSETS_CSS_PATH + '/embedpress.css?v=' + self.params.versionUID, editorInstance, editorInstance);
831 self.addEvent('nodechange', editorInstance, self.onNodeChange);
832 self.addEvent('keydown', editorInstance, function (e) {
833 self.onKeyDown(e, editorInstance);
834 });
835
836 var onUndoCallback = function (e) {
837 self.onUndo(e, editorInstance);
838 };
839
840 self.addEvent('undo', editorInstance, onUndoCallback); // TinyMCE
841 self.addEvent('undo', editorInstance.undoManager, onUndoCallback); // JCE
842
843 var doc = editorInstance.getDoc();
844 $(doc).on('mouseenter', '.embedpress_wrapper', function (e) {
845 self.onMouseEnter(e, editorInstance);
846 });
847 $(doc).on('mouseout', '.embedpress_wrapper', self.onMouseOut);
848 $(doc).on('mousedown', '.embedpress_wrapper > .embedpress_controller_panel', function (e) {
849 self.cancelEvent(e, editorInstance);
850 });
851 doc = null;
852
853 // Add the node filter that will convert the url into the preview box for the embed code
854 editorInstance.parser.addNodeFilter('#text', function addNodeFilterIntoParser (nodes, arg) {
855 self.each(nodes, function eachNodeInParser (node) {
856 // Stop if the node is "isolated". It would generate an error in the browser console and break.
857 if (node.parent === null && node.prev === null) {
858 return;
859 }
860
861 var subject = node.value.trim();
862
863 if (!subject.isValidUrl()) {
864 if (!subject.match(SHORTCODE_REGEXP)) {
865 return;
866 }
867 }
868 subject = self.decodeEmbedURLSpecialChars(subject);
869 if (!subject.isValidUrl()) {
870 if (!subject.match(SHORTCODE_REGEXP)) {
871 return;
872 }
873 }
874
875 subject = node.value.stripShortcode(embedpressPreviewData.shortcode).trim();
876
877 // These patterns need to have groups for the pre and post texts
878 // @TODO: maybe remove this list of URLs? Let the server side code decide what URL should be parsed
879 var patterns = self.getProvidersURLPatterns();
880
881 (function tryToMatchContentAgainstUrlPatternWithIndex (urlPatternIndex) {
882 if (urlPatternIndex < patterns.length) {
883 var urlPattern = patterns[urlPatternIndex];
884 var urlPatternRegex = new RegExp(urlPattern);
885
886 var url = self.decodeEmbedURLSpecialChars(subject).trim();
887
888 var matches = url.match(urlPatternRegex);
889 // Check if content matches the url pattern.
890 if (matches && matches !== null && !!matches.length) {
891 url = self.encodeEmbedURLSpecialChars(matches[2]);
892
893 var wrapper = self.addURLsPlaceholder(node, url, editorInstance);
894
895 setTimeout(function () {
896 var doc = editorInstance.getDoc();
897
898 if (doc === null) {
899 return;
900 }
901
902 var previewWrapper = $(doc.querySelector('#' + wrapper.attributes.map['id']));
903 var previewWrapperParent = $(previewWrapper.parent());
904
905 if (previewWrapperParent && previewWrapperParent.prop('tagName') && previewWrapperParent.prop('tagName').toUpperCase() === 'P') {
906 previewWrapperParent.replaceWith(previewWrapper);
907 }
908
909 var previewWrapperOlderSibling = previewWrapper.prev();
910 if (previewWrapperOlderSibling && previewWrapperOlderSibling.prop('tagName') && previewWrapperOlderSibling.prop('tagName').toUpperCase() === 'P' && !previewWrapperOlderSibling.html().replace(/\&nbsp\;/i, '').length) {
911 previewWrapperOlderSibling.remove();
912 } else {
913 if (typeof previewWrapperOlderSibling.html() !== 'undefined') {
914 if (previewWrapperOlderSibling.html().match(/<[\/]?br>/)) {
915 if (!previewWrapperOlderSibling.prev().length) {
916 previewWrapperOlderSibling.remove();
917 }
918 }
919 }
920 }
921
922 var previewWrapperYoungerSibling = previewWrapper.next();
923 if (previewWrapperYoungerSibling && previewWrapperYoungerSibling.length && previewWrapperYoungerSibling.prop('tagName').toUpperCase() === 'P') {
924 if (!previewWrapperYoungerSibling.next().length && !previewWrapperYoungerSibling.html().replace(/\&nbsp\;/i, '').length) {
925 previewWrapperYoungerSibling.remove();
926 $('<p>&nbsp;</p>').insertAfter(previewWrapper);
927 }
928 } else {
929 $('<p>&nbsp;</p>').insertAfter(previewWrapper);
930 }
931
932 setTimeout(function () {
933 editorInstance.selection.select(editorInstance.getBody(), true);
934 editorInstance.selection.collapse(false);
935 }, 50);
936 }, 50);
937 } else {
938 // No match. So we move on to check the next url pattern.
939 tryToMatchContentAgainstUrlPatternWithIndex(urlPatternIndex + 1);
940 }
941 }
942 })(0);
943 });
944 });
945
946 // Add the filter that will convert the preview box/embed code back to the raw url
947 editorInstance.serializer.addNodeFilter('div', function addNodeFilterIntoSerializer (nodes, arg) {
948 self.each(nodes, function eachNodeInSerializer (node) {
949 // Stop if the node is "isolated". It would generate an error in the browser console and break.
950 if (node.parent === null && node.prev === null) {
951 return;
952 }
953
954 var nodeClasses = (node.attributes.map.class || '').split(' ');
955 var wrapperFactoryClasses = ['embedpress_wrapper', 'embedpress_placeholder', 'wpview', 'wpview-wrap'];
956
957 var isWrapped = nodeClasses.filter(function (n) {
958 return wrapperFactoryClasses.indexOf(n) != -1;
959 }).length > 0;
960
961 if (isWrapped) {
962 var factoryAttributes = ['id', 'style', 'data-loading-text', 'data-uid', 'data-url'];
963 var customAttributes = {};
964 var dataPrefix = 'data-';
965 for (var attr in node.attributes.map) {
966 if (attr.toLowerCase() !== 'class') {
967 if (factoryAttributes.indexOf(attr) < 0) {
968 // Remove the "data-" prefix for more readability
969 customAttributes[attr.replace(dataPrefix, '')] = node.attributes.map[attr];
970 }
971 } else {
972 var customClasses = [];
973 for (var wrapperClassIndex in nodeClasses) {
974 var wrapperClass = nodeClasses[wrapperClassIndex];
975 if (wrapperFactoryClasses.indexOf(wrapperClass) === -1) {
976 customClasses.push(wrapperClass);
977 }
978 }
979
980 if (!!customClasses.length) {
981 customAttributes.class = customClasses.join(' ');
982 }
983 }
984 }
985
986 var p = new self.Node('p', 1);
987
988 var text = new self.Node('#text', 3);
989 text.value = (node.attributes.map && typeof node.attributes.map['data-url'] != 'undefined') ? self.decodeEmbedURLSpecialChars(node.attributes.map['data-url'].trim(), true, customAttributes) : '';
990
991 p.append(text.clone());
992
993 node.replace(text);
994 text.replace(p);
995 }
996 });
997 });
998
999 editorInstance.serializer.addNodeFilter('p', function addNodeFilterIntoSerializer (nodes, arg) {
1000 self.each(nodes, function eachNodeInSerializer (node) {
1001 if (node.firstChild == node.lastChild) {
1002 if (node.firstChild && 'value' in node.firstChild && (node.firstChild.value === '&nbsp;' || !node.firstChild.value.trim().length)) {
1003 node.remove();
1004 }
1005 }
1006 });
1007 });
1008
1009 //@todo:isthiseachreallynecessary?
1010 // Add event to reconfigure wrappers every time the content is loaded
1011 tinymce.each(tinymce.editors, function onEachEditor (editor) {
1012 self.addEvent('loadContent', editor, function onInitEditor (ed) {
1013 self.configureWrappers(editor);
1014 });
1015 });
1016
1017 // Add the edit form
1018
1019 // @todo: This is needed only for JCE, to fix the img placeholder. Try to find out a better approach to avoid the placeholder blink
1020 window.setTimeout(
1021 function afterTimeoutOnFindEditor () {
1022 /*
1023 * This is required because after load/refresh the page, the
1024 * onLoadContent is not being triggered automatically, so
1025 * we force the event
1026 */
1027 editorInstance.load();
1028 },
1029 // If in JCE the user see the placeholder (img) instead of the iframe after load/refresh the pagr, this time is too short
1030 500
1031 );
1032 }
1033
1034 // Let's make sure the inner doc has been fully loaded first.
1035 var checkTimesLimit = 100;
1036 var checkIndex = 0;
1037 var statusCheckerInterval = setInterval(function () {
1038 if (checkIndex === checkTimesLimit) {
1039 clearInterval(statusCheckerInterval);
1040 alert('For some reason TinyMCE was not fully loaded yet. Please, refresh the page and try again.');
1041 } else {
1042 var doc = editorInstance.getDoc();
1043 if (doc) {
1044 clearInterval(statusCheckerInterval);
1045 onFindEditorCallback();
1046 } else {
1047 checkIndex++;
1048 }
1049 }
1050 }, 250);
1051 };
1052
1053 self.fixIframeSize = function (iframe) {
1054 var maxWidth = 480;
1055 if ($(iframe).width() > maxWidth && !$(iframe).data('size-fixed')) {
1056 var ratio = $(iframe).height() / $(iframe).width();
1057 $(iframe).width(maxWidth);
1058 $(iframe).height(maxWidth * ratio);
1059 $(iframe).css('max-width', maxWidth);
1060 $(iframe).attr('max-width', maxWidth);
1061
1062 $(iframe).data('size-fixed', true);
1063 }
1064 };
1065
1066 /**
1067 * Function triggered on mouse enter the wrapper
1068 *
1069 * @param object e The event
1070 * @return void
1071 */
1072 self.onMouseEnter = function (e, editorInstance) {
1073 self.displayPreviewControllerPanel($(e.currentTarget), editorInstance);
1074 };
1075
1076 /**
1077 * Function triggered on mouse get out of the wrapper
1078 *
1079 * @param object e The event
1080 * @return void
1081 */
1082 self.onMouseOut = function (e) {
1083 // Check if the destiny is not a child element
1084 // Chrome
1085 if (self.isDefined(e.toElement)) {
1086 if (e.toElement.parentElement == e.fromElement
1087 || $(e.toElement).hasClass('embedpress_ignore_mouseout')
1088 ) {
1089 return false;
1090 }
1091 }
1092
1093 // Firefox
1094 if (self.isDefined(e.relatedTarget)) {
1095 if ($(e.relatedTarget).hasClass('embedpress_ignore_mouseout')) {
1096 return false;
1097 }
1098 }
1099
1100 self.hidePreviewControllerPanel();
1101 };
1102
1103 /**
1104 * Callback triggered by paste events. This should be hooked by TinyMCE's paste_preprocess
1105 * setting. A normal bind to the onPaste event doesn't work correctly all the times
1106 * (specially when you copy and paste content from the same editor).
1107 *
1108 * @param mixed - plugin
1109 * @param mixed - args
1110 *
1111 * @return void
1112 */
1113
1114 self.onPaste = function (plugin, args) {
1115 var urlPatternRegex = new RegExp(/(https?):\/\/([w]{3}\.)?.+?(?:\s|$)/i);
1116 var urlPatternsList = self.getProvidersURLPatterns();
1117
1118 // Split the pasted content into separated lines.
1119 var contentLines = args.content.split(/\n/g) || [];
1120 contentLines = contentLines.map(function (line, itemIndex) {
1121 // Check if there's a url into `line`.
1122 if (line.match(urlPatternRegex)) {
1123 // Split the current line across its space-characters to isolate the url.
1124 let termsList = line.trim().split(/\s+/);
1125 termsList = termsList.map(function (term, termIndex) {
1126 // Check if the term into the current line is a url.
1127 var match = term.match(urlPatternRegex);
1128 if (match) {
1129 for (var urlPatternIndex = 0; urlPatternIndex < urlPatternsList.length; urlPatternIndex++) {
1130 // Isolates that url from the rest of the content if the service is supported.
1131 var urlPattern = new RegExp(urlPatternsList[urlPatternIndex]);
1132 if (urlPattern.test(term)) {
1133 return '</p><p>' + match[0] + '</p><p>';
1134 }
1135 }
1136 }
1137
1138 return term;
1139 });
1140
1141 termsList[termsList.length - 1] = termsList[termsList.length - 1] + '<br>';
1142
1143 line = termsList.join(' ');
1144 }
1145
1146 return line;
1147 });
1148
1149 // Check if the text was transformed or not. If it was, add wrappers
1150 var content = contentLines.join('');
1151
1152 if (content.replace(/<br>$/, '') !== args.content) {
1153 args.content = '<p>' + args.content + '</p>';
1154 }
1155 };
1156
1157 /**
1158 * Method trigered on every node change, to detect new lines. It will
1159 * try to fix a default behavior for some editors of clone the parent
1160 * element when adding a line break. This will clone the embed wrapper
1161 * if we set the cursor after a preview wrapper and hit enter.
1162 *
1163 * @param object e The event
1164 * @return void
1165 */
1166 self.onNodeChange = function (e) {
1167 // Fix the clone parent on break lines issue
1168 // Check if a line break was added
1169 if (e.element.tagName === 'BR') {
1170 // Check one of the parent elements is a clonned embed wrapper
1171 if (e.parents.length > 0) {
1172 $.each(e.parents, function (index, parent) {
1173 if ($(parent).hasClass('embedpress_wrapper')) {
1174 // Remove the cloned wrapper and replace with a 'br' tag
1175 $(parent).replaceWith($('<br>'));
1176 }
1177 });
1178 }
1179 } else if (e.element.tagName === 'IFRAME') {
1180 if (e.parents.length > 0) {
1181 $.each(e.parents, function (index, parent) {
1182 parent = $(parent);
1183 if (parent.hasClass('embedpress_wrapper')) {
1184 var wrapper = $('.embedpress-wrapper', parent);
1185 if (wrapper.length > 1) {
1186 wrapper.get(0).remove();
1187 }
1188 }
1189 });
1190 }
1191 }
1192 };
1193
1194 self.onKeyDown = function (e, editorInstance) {
1195 var node = editorInstance.selection.getNode();
1196
1197 if (e.keyCode == 8 || e.keyCode == 46) {
1198 if (node.nodeName.toLowerCase() === 'p') {
1199 var children = $(node).children();
1200 if (children.length > 0) {
1201 $.each(children, function () {
1202 // On delete, make sure to remove the wrapper and children, not only the wrapper
1203 if ($(this).hasClass('embedpress_wrapper') || $(this).hasClass('embedpress_ignore_mouseout')) {
1204 $(this).remove();
1205
1206 editorInstance.focus();
1207 }
1208 });
1209 }
1210 }
1211 } else {
1212 // Ignore the arrows keys
1213 var arrowsKeyCodes = [37, 38, 39, 40];
1214 if (arrowsKeyCodes.indexOf(e.keyCode) == -1) {
1215
1216 // Check if we are inside a preview wrapper
1217 if ($(node).hasClass('embedpress_wrapper') || $(node).hasClass('embedpress_ignore_mouseout')) {
1218 // Avoid delete the wrapper or block line break if we are inside the wrapper
1219 if (e.keyCode == 13) {
1220 wrapper = $(self.getWrapperFromChild(node));
1221 if (wrapper.length > 0) {
1222 // Creates a temporary element which will be inserted after the wrapper
1223 var tmpId = '__embedpress__tmp_' + self.makeId();
1224 wrapper.after($('<span id="' + tmpId + '"></span>'));
1225 // Get and select the temporary element
1226 var span = editorInstance.dom.select('span#' + tmpId)[0];
1227 editorInstance.selection.select(span);
1228 // Remove the temporary element
1229 $(span).remove();
1230 }
1231
1232 return true;
1233 } else {
1234 // If we are inside the embed preview, ignore any key to avoid edition
1235 return self.cancelEvent(e, editorInstance);
1236 }
1237 }
1238 }
1239 }
1240
1241 return true;
1242 };
1243
1244 self.getWrapperFromChild = function (element) {
1245 // Is the wrapper
1246 if ($(element).hasClass('embedpress_wrapper')) {
1247 return element;
1248 } else {
1249 var $parent = $(element).parent();
1250
1251 if ($parent.length > 0) {
1252 return self.getWrapperFromChild($parent[0]);
1253 }
1254 }
1255
1256 return false;
1257 };
1258
1259 self.onUndo = function (e, editorInstance) {
1260 // Force re-render everything
1261 editorInstance.load();
1262 };
1263
1264 self.cancelEvent = function (e, editorInstance) {
1265 e.preventDefault();
1266 e.stopPropagation();
1267 editorInstance.dom.events.cancel();
1268
1269 return false;
1270 };
1271
1272 /**
1273 * Method executed when the edit button is clicked. It will display
1274 * a field with the current url, to update the current embed's source
1275 * url.
1276 *
1277 * @param Object e The event
1278 * @return void
1279 */
1280 self.onClickEditButton = function (e, editorInstance) {
1281 // Prevent edition of the panel
1282 self.cancelEvent(e, editorInstance);
1283
1284 self.activeWrapperForModal = self.activeWrapper;
1285
1286 var $wrapper = self.activeWrapperForModal;
1287 var wrapperUid = $wrapper.prop('id').replace('embedpress_wrapper_', '');
1288
1289 var customAttributes = {};
1290
1291 var $embedInnerWrapper = $('.embedpress-wrapper', $wrapper);
1292 var embedItem = $('iframe', $wrapper);
1293 if (!embedItem.length) {
1294 embedItem = null;
1295 }
1296 if ($embedInnerWrapper.length > 1){
1297 $.each($embedInnerWrapper[0].attributes, function () {
1298 if (this.specified) {
1299 if (this.name !== 'class') {
1300 customAttributes[this.name.replace('data-', '').toLowerCase()] = this.value;
1301 }
1302 }
1303 });
1304 }
1305
1306
1307 var embedWidth = (((embedItem && embedItem.width()) || $embedInnerWrapper.data('width')) || $embedInnerWrapper.width()) || '';
1308 var embedHeight = (((embedItem && embedItem.height()) || $embedInnerWrapper.data('height')) || $embedInnerWrapper.height()) || '';
1309
1310 embedItem = $embedInnerWrapper = null;
1311
1312 $('<div class="loader-indicator"><i class="embedpress-icon-reload"></i></div>').appendTo($wrapper);
1313
1314 setTimeout(function () {
1315 $.ajax({
1316 type: 'GET',
1317 url: self.params.baseUrl + 'wp-admin/admin-ajax.php',
1318 data: {
1319 action: 'embedpress_get_embed_url_info',
1320 url: self.decodeEmbedURLSpecialChars($wrapper.data('url'), false)
1321 },
1322 beforeSend: function (request, requestSettings) {
1323 $('.loader-indicator', $wrapper).addClass('is-loading');
1324 },
1325 success: function (response) {
1326 if (!response) {
1327 alert('Unable to get a valid response from the server.');
1328 return;
1329 }
1330 if (response.canBeResponsive) {
1331 var embedShouldBeResponsive = true;
1332 if ('width' in customAttributes || 'height' in customAttributes) {
1333 embedShouldBeResponsive = false;
1334 } else if ('responsive' in customAttributes && customAttributes['responsive'].isFalse()) {
1335 embedShouldBeResponsive = false;
1336 }
1337 }
1338
1339 // Create modern modal dialog
1340 var modalHtml = '<div class="embedpress-modal-overlay" id="modal-' + wrapperUid + '">' +
1341 '<div class="embedpress-modal">' +
1342 '<div class="embedpress-modal-header">' +
1343 '<h3>Editing Embed properties</h3>' +
1344 '<button type="button" class="embedpress-modal-close">&times;</button>' +
1345 '</div>' +
1346 '<div class="embedpress-modal-body">' +
1347 '<form id="form-' + wrapperUid + '" class="embedpress-form">' +
1348 '<div class="embedpress-form-group">' +
1349 '<label for="input-url-' + wrapperUid + '">URL</label>' +
1350 '<input type="url" id="input-url-' + wrapperUid + '" value="' + self.decodeEmbedURLSpecialChars($wrapper.data('url'), false) + '">' +
1351 '</div>' +
1352 (response.canBeResponsive ?
1353 '<div class="embedpress-form-group">' +
1354 '<label>Responsive</label>' +
1355 '<div class="embedpress-radio-group">' +
1356 '<label><input type="radio" name="input-responsive-' + wrapperUid + '" value="1"' + (embedShouldBeResponsive ? ' checked' : '') + '> Yes</label>' +
1357 '<label><input type="radio" name="input-responsive-' + wrapperUid + '" value="0"' + (!embedShouldBeResponsive ? ' checked' : '') + '> No</label>' +
1358 '</div>' +
1359 '</div>' : '') +
1360 '<div class="embedpress-form-row">' +
1361 '<div class="embedpress-form-group">' +
1362 '<label for="input-width-' + wrapperUid + '">Width</label>' +
1363 '<input type="number" id="input-width-' + wrapperUid + '" value="' + embedWidth + '"' + (embedShouldBeResponsive ? ' disabled' : '') + '>' +
1364 '</div>' +
1365 '<div class="embedpress-form-group">' +
1366 '<label for="input-height-' + wrapperUid + '">Height</label>' +
1367 '<input type="number" id="input-height-' + wrapperUid + '" value="' + embedHeight + '"' + (embedShouldBeResponsive ? ' disabled' : '') + '>' +
1368 '</div>' +
1369 '</div>' +
1370 '</form>' +
1371 '</div>' +
1372 '<div class="embedpress-modal-footer">' +
1373 '<button type="button" class="embedpress-btn embedpress-btn-cancel">Cancel</button>' +
1374 '<button type="button" class="embedpress-btn embedpress-btn-primary">Save</button>' +
1375 '</div>' +
1376 '</div>' +
1377 '</div>';
1378
1379 // Add modal to page
1380 $('body').append(modalHtml);
1381 var $modal = $('#modal-' + wrapperUid);
1382
1383 // Show modal
1384 $modal.show();
1385
1386 // Handle modal events
1387 $modal.on('click', '.embedpress-modal-close, .embedpress-btn-cancel', function() {
1388 $modal.remove();
1389 self.activeWrapperForModal = null;
1390 });
1391
1392 $modal.on('click', '.embedpress-btn-primary', function() {
1393 var $wrapper = self.activeWrapperForModal;
1394
1395 // Select the current wrapper as a base for the new element
1396 editorInstance.focus();
1397 editorInstance.selection.select($wrapper[0]);
1398
1399 $wrapper.children().remove();
1400 $wrapper.remove();
1401
1402 if (response.canBeResponsive) {
1403 if ($('#form-' + wrapperUid + ' input[name="input-responsive-' + wrapperUid + '"]:checked').val() === '0') {
1404 var embedCustomWidth = $('#input-width-' + wrapperUid).val();
1405 if (parseInt(embedCustomWidth) > 0) {
1406 customAttributes['width'] = embedCustomWidth;
1407 }
1408
1409 var embedCustomHeight = $('#input-height-' + wrapperUid).val();
1410 if (parseInt(embedCustomHeight) > 0) {
1411 customAttributes['height'] = embedCustomHeight;
1412 }
1413
1414 customAttributes['responsive'] = 'false';
1415 } else {
1416 delete customAttributes['width'];
1417 delete customAttributes['height'];
1418
1419 customAttributes['responsive'] = 'true';
1420 }
1421 } else {
1422 var embedCustomWidth = $('#input-width-' + wrapperUid).val();
1423 if (parseInt(embedCustomWidth) > 0) {
1424 customAttributes['width'] = embedCustomWidth;
1425 }
1426
1427 var embedCustomHeight = $('#input-height-' + wrapperUid).val();
1428 if (parseInt(embedCustomHeight) > 0) {
1429 customAttributes['height'] = embedCustomHeight;
1430 }
1431 }
1432
1433 var customAttributesList = [];
1434 if (!!Object.keys(customAttributes).length) {
1435 for (var attrName in customAttributes) {
1436 customAttributesList.push(attrName + '="' + customAttributes[attrName] + '"');
1437 }
1438 }
1439
1440 var shortcode = '[' + embedpressPreviewData.shortcode + (customAttributesList.length > 0 ? ' ' + customAttributesList.join(' ') : '') + ']' + $('#input-url-' + wrapperUid).val() + '[/' + embedpressPreviewData.shortcode + ']';
1441 // We do not directly replace the node because it was causing a bug on a second edit attempt
1442 editorInstance.execCommand('mceInsertContent', false, shortcode);
1443
1444 self.configureWrappers(editorInstance);
1445 $modal.remove();
1446 });
1447
1448 $modal.on('change', 'input[type="radio"]', function (e) {
1449 var $radio = $(this);
1450 var form = $radio.closest('form');
1451 var isResponsive = $radio.val() === '1';
1452
1453 $('input[type="number"]', form).prop('disabled', isResponsive);
1454 });
1455 },
1456 complete: function (request, textStatus) {
1457 $('.loader-indicator', $wrapper).removeClass('is-loading');
1458
1459 setTimeout(function () {
1460 $('.loader-indicator', $wrapper).remove();
1461 }, 350);
1462 },
1463 dataType: 'json',
1464 async: true
1465 });
1466 }, 200);
1467
1468 return false;
1469 };
1470
1471 /**
1472 * Method executed when the remove button is clicked. It will remove
1473 * the preview and embed code, adding a mark to ignore the url
1474 *
1475 * @param Object e The event
1476 * @return void
1477 */
1478 self.onClickRemoveButton = function (e, editorInstance) {
1479 // Prevent edition of the panel
1480 self.cancelEvent(e, editorInstance);
1481
1482 var $wrapper = self.activeWrapper;
1483
1484 $wrapper.children().remove();
1485 $wrapper.remove();
1486
1487 return false;
1488 };
1489
1490 self.recursivelyAddClass = function (element, className) {
1491 $(element).children().each(function (index, child) {
1492 $(child).addClass(className);
1493
1494 var grandChild = $(child).children();
1495 if (grandChild.length > 0) {
1496 self.recursivelyAddClass(child, className);
1497 }
1498 });
1499 };
1500
1501 self.setInterval = function (callback, time, timeout) {
1502 var elapsed = 0;
1503 var iteraction = 0;
1504
1505 var interval = window.setInterval(function () {
1506 elapsed += time;
1507 iteraction++;
1508
1509 if (elapsed <= timeout) {
1510 callback(iteraction, elapsed);
1511 } else {
1512 self.stopInterval(interval);
1513 }
1514 }, time);
1515
1516 return interval;
1517 };
1518
1519 self.stopInterval = function (interval) {
1520 window.clearInterval(interval);
1521 interval = null;
1522 };
1523
1524 /**
1525 * Configure unconfigured embed wrappers, adding events and css
1526 * @return void
1527 */
1528 self.configureWrappers = function (editorInstance) {
1529 window.setTimeout(
1530 function configureWrappersTimeOut () {
1531 var doc = editorInstance.getDoc(),
1532 $wrapper = null;
1533
1534 // Get all the wrappers
1535 var wrappers = doc.getElementsByClassName('embedpress_wrapper');
1536 if (wrappers.length > 0) {
1537 for (var i = 0; i < wrappers.length; i++) {
1538 $wrapper = $(wrappers[i]);
1539
1540 // Check if the wrapper wasn't already configured
1541 if ($wrapper.data('configured') != true) {
1542 // A timeout was set to avoid block the content loading
1543 window.setTimeout(function () {
1544 // @todo: Check if we need a limit of levels to avoid use too much resources
1545 self.recursivelyAddClass($wrapper, 'embedpress_ignore_mouseout');
1546 }, 500);
1547
1548 // Fix the wrapper size. Wait until find the child iframe. L
1549 var interval = self.setInterval(function (iteraction) {
1550 var $childIframes = $wrapper.find('iframe');
1551
1552 if ($childIframes.length > 0) {
1553 $.each($childIframes, function (index, iframe) {
1554 // Facebook has more than one iframe, we need to ignore the Cross Domain Iframes
1555 if ($(iframe).attr('id') !== 'fb_xdm_frame_https'
1556 && $(iframe).attr('id') !== 'fb_xdm_frame_http'
1557 ) {
1558 $wrapper.css('width', $(iframe).width() + 'px');
1559 self.stopInterval(interval);
1560 }
1561 });
1562 }
1563 }, 500, 8000);
1564
1565 $wrapper.data('configured', true);
1566 }
1567 }
1568 }
1569 },
1570 200
1571 );
1572 };
1573
1574 /**
1575 * Hide the controller panel
1576 *
1577 * @return void
1578 */
1579 self.hidePreviewControllerPanel = function () {
1580 if (self.controllerPanelIsActive()) {
1581 $(self.activeControllerPanel).addClass('hidden');
1582 self.activeControllerPanel = null;
1583 self.activeWrapper = null;
1584 }
1585 };
1586
1587 /**
1588 * Get an element by id in the editor's content
1589 *
1590 * @param String id The element id
1591 * @return Element The found element or null, wrapped by jQuery
1592 */
1593 self.getElementInContentById = function (id, editorInstance) {
1594 var doc = editorInstance.getDoc();
1595
1596 if (doc === null) {
1597 return;
1598 }
1599
1600 return $(doc.getElementById(id));
1601 };
1602
1603 /**
1604 * Show the controller panel
1605 *
1606 * @param element $wrapper The wrapper which will be activate
1607 * @return void
1608 */
1609 self.displayPreviewControllerPanel = function ($wrapper, editorInstance) {
1610 if (self.controllerPanelIsActive() && $wrapper !== self.activeWrapper) {
1611 self.hidePreviewControllerPanel();
1612 }
1613
1614 if (!self.controllerPanelIsActive() && !$wrapper.hasClass('is-loading')) {
1615 var uid = $wrapper.data('uid');
1616 var $panel = self.getElementInContentById('embedpress_controller_panel_' + uid, editorInstance);
1617
1618 if (!$panel.data('event-set')) {
1619 var $editButton = self.getElementInContentById('embedpress_button_edit_' + uid, editorInstance);
1620 var $removeButton = self.getElementInContentById('embedpress_button_remove_' + uid, editorInstance);
1621
1622 self.addEvent('mousedown', $editButton, function (e) {
1623 self.onClickEditButton(e, editorInstance);
1624 });
1625
1626 self.addEvent('mousedown', $removeButton, function (e) {
1627 self.onClickRemoveButton(e, editorInstance);
1628 });
1629
1630 $panel.data('event-set', true);
1631 }
1632
1633 // Update the position of the control bar
1634 var next = $panel.next()[0];
1635 if (typeof next !== 'undefined') {
1636 if (next.nodeName.toLowerCase() === 'iframe') {
1637 $panel.css('left', ($(next).width() / 2));
1638 }
1639 }
1640
1641 // Show the bar
1642 $panel.removeClass('hidden');
1643
1644 self.activeControllerPanel = $panel;
1645 self.activeWrapper = $wrapper;
1646 }
1647 };
1648 };
1649
1650 if (!window.EmbedPress) {
1651 window.EmbedPress = new EmbedPress();
1652 }
1653
1654 // Initialize EmbedPress for all the current editors.
1655 window.EmbedPress.init(embedpressPreviewData.previewSettings);
1656 });
1657 })(jQuery, String, embedpressPreviewData);
1658