PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 3.9.7
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v3.9.7
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
vendor 7 years ago admin.js 2 years ago ads.js 2 years ago documents-viewer-script.js 3 years ago front.js 2 years ago index.html 7 years ago initplyr.js 2 years ago pdfobject.min.js 3 years ago plyr.polyfilled.js 3 years ago preview.js 3 years ago settings.js 6 years ago vimeo-player.js 2 years ago ytiframeapi.js 2 years 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, $data, 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('\\[\/?' + $data.EMBEDPRESS_SHORTCODE + '\\]', 'gi');
151
152 var EmbedPress = function () {
153 var self = this;
154
155 var PLG_SYSTEM_ASSETS_CSS_PATH = $data.EMBEDPRESS_URL_ASSETS + '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 },
352 success: onsuccess,
353 dataType: 'json',
354 async: true
355 });
356 };
357
358 self.addStylesheet = function (url, editorInstance) {
359 var head = editorInstance.getDoc().getElementsByTagName('head')[0];
360
361 var $style = $('<link rel="stylesheet" type="text/css" href="' + url + '">');
362 $style.appendTo(head);
363 };
364
365 self.convertURLSchemeToPattern = function (scheme) {
366 var prefix = '(.*)((?:http|embedpress)s?:\\/\\/(?:www\\.)?',
367 suffix = '[\\/]?)(.*)',
368 pattern;
369
370 scheme = scheme.replace(/\*/g, '[a-zA-Z0-9=&_\\-\\?\\.\\/!\\+%:@,#]+');
371 scheme = scheme.replace(/\./g, '\\.');
372 scheme = scheme.replace(/\//g, '\\/');
373
374 return prefix + scheme + suffix;
375 };
376
377 self.getProvidersURLPatterns = function () {
378 // @todo: Add option to disable/enable the providers
379 var patterns = [];
380
381 self.each($data.urlSchemes, function convertEachURLSchemesToPattern (scheme) {
382 patterns.push(self.convertURLSchemeToPattern(scheme));
383 });
384
385 return patterns;
386 };
387
388 self.addScript = function (source, callback, wrapper, editorInstance) {
389 var doc = editorInstance.getDoc();
390
391 if (typeof wrapper === 'undefined' || !wrapper) {
392 wrapper = $(doc.getElementsByTagName('head')[0]);
393 }
394
395 var $script = $(doc.createElement('script'));
396 $script.attr('async', 1);
397
398 if (typeof callback === 'function') {
399 $script.ready(callback);
400 }
401
402 $script.attr('src', source);
403
404 wrapper.append($script);
405 };
406
407 self.addScriptDeclaration = function (wrapper, declaration, editorInstance) {
408 var doc = editorInstance.getDoc(),
409 $script = $(doc.createElement('script'));
410
411 $(wrapper).append($script);
412
413 $script.text(declaration);
414 };
415
416 self.addURLsPlaceholder = function (node, url, editorInstance) {
417 var uid = self.makeId();
418
419 var wrapperClasses = ['embedpress_wrapper', 'embedpress_placeholder', 'wpview', 'wpview-wrap'];
420
421 var shortcodeAttributes = node.value.getShortcodeAttributes($data.EMBEDPRESS_SHORTCODE);
422 var customAttributes = shortcodeAttributes;
423
424 var customClasses = '';
425 if (!!Object.keys(shortcodeAttributes).length) {
426 var specialAttributes = ['class', 'href', 'data-href'];
427 // Iterates over each attribute of shortcodeAttributes to add the prefix "data-" if missing
428 var dataPrefix = 'data-';
429 var prefixedShortcodeAttributes = [];
430 for (var attr in shortcodeAttributes) {
431 if (specialAttributes.indexOf(attr) === -1) {
432 if (attr.indexOf(dataPrefix) !== 0) {
433 prefixedShortcodeAttributes[dataPrefix + attr] = shortcodeAttributes[attr];
434 } else {
435 prefixedShortcodeAttributes[attr] = shortcodeAttributes[attr];
436 }
437 } else {
438 attr = attr.replace(dataPrefix, '');
439 if (attr === 'class') {
440 wrapperClasses.push(shortcodeAttributes[attr]);
441 }
442 }
443 }
444
445 shortcodeAttributes = prefixedShortcodeAttributes;
446 prefixedShortcodeAttributes = dataPrefix = null;
447 }
448
449 if (('data-width' in shortcodeAttributes || 'data-height' in shortcodeAttributes) && 'data-responsive' in shortcodeAttributes) {
450 shortcodeAttributes['data-responsive'] = 'false';
451 }
452
453 var wrapper = new self.Node('div', 1);
454 var wrapperSettings = {
455 'class': Array.from(new Set(wrapperClasses)).join(' '),
456 'data-url': url,
457 'data-uid': uid,
458 'id': 'embedpress_wrapper_' + uid,
459 'data-loading-text': 'Loading your embed...'
460 };
461
462 wrapperSettings = $.extend({}, wrapperSettings, shortcodeAttributes);
463
464 if (wrapperSettings.class.indexOf('is-loading') === -1) {
465 wrapperSettings.class += ' is-loading';
466 }
467
468 wrapper.attr(wrapperSettings);
469
470 var panel = new self.Node('div', 1);
471 panel.attr({
472 'id': 'embedpress_controller_panel_' + uid,
473 'class': 'embedpress_controller_panel embedpress_ignore_mouseout hidden'
474 });
475 wrapper.append(panel);
476
477 function createGhostNode (htmlTag, content) {
478 htmlTag = htmlTag || 'span';
479 content = content || '&nbsp;';
480
481 var ghostNode = new self.Node(htmlTag, 1);
482 ghostNode.attr({
483 'class': 'hidden'
484 });
485
486 var ghostText = new self.Node('#text', 3);
487 ghostText.value = content;
488 ghostNode.append(ghostText);
489
490 return ghostNode;
491 }
492
493 var editButton = new self.Node('div', 1);
494 editButton.attr({
495 'id': 'embedpress_button_edit_' + uid,
496 'class': 'embedpress_ignore_mouseout embedpress_controller_button'
497 });
498 var editButtonIcon = new self.Node('div', 1);
499 editButtonIcon.attr({
500 'class': 'embedpress-icon-pencil embedpress_ignore_mouseout'
501 });
502 editButtonIcon.append(createGhostNode());
503 editButton.append(editButtonIcon);
504 panel.append(editButton);
505
506 var removeButton = new self.Node('div', 1);
507 removeButton.attr({
508 'id': 'embedpress_button_remove_' + uid,
509 'class': 'embedpress_ignore_mouseout embedpress_controller_button'
510 });
511 var removeButtonIcon = new self.Node('div', 1);
512 removeButtonIcon.attr({
513 'class': 'embedpress-icon-x embedpress_ignore_mouseout'
514 });
515 removeButtonIcon.append(createGhostNode());
516 removeButton.append(removeButtonIcon);
517 panel.append(removeButton);
518
519 node.value = node.value.trim();
520
521 node.replace(wrapper);
522
523 // Trigger the timeout which will load the content
524 window.setTimeout(function () {
525 self.parseContentAsync(uid, url, customAttributes, editorInstance);
526 }, 200);
527
528 return wrapper;
529 };
530
531 self.parseContentAsync = function (uid, url, customAttributes, editorInstance) {
532 customAttributes = typeof customAttributes === 'undefined' ? {} : customAttributes;
533
534 url = self.decodeEmbedURLSpecialChars(url, true, customAttributes);
535 var rawUrl = url.stripShortcode($data.EMBEDPRESS_SHORTCODE);
536
537 $(self).triggerHandler('EmbedPress.beforeEmbed', {
538 'url': rawUrl,
539 'meta': {
540 'attributes': customAttributes || {}
541 }
542 });
543
544 // Get the parsed embed code from the EmbedPress plugin
545 self.getParsedContent(url, function getParsedContentCallback (result) {
546 var embeddedContent = (typeof result.data === 'object' ? result.data.embed : result.data).stripShortcode($data.EMBEDPRESS_SHORTCODE);
547 var $wrapper = $(self.getElementInContentById('embedpress_wrapper_' + uid, editorInstance));
548 var wrapperParent = $($wrapper.parent());
549
550 // Check if $wrapper was rendered inside a <p> element.
551 if (wrapperParent.prop('tagName') && wrapperParent.prop('tagName').toUpperCase() === 'P') {
552 wrapperParent.replaceWith($wrapper);
553 // Check if there's at least one "space" after $wrapper.
554 var nextSibling = $($wrapper).next();
555 if (!nextSibling.length || nextSibling.prop('tagName').toUpperCase() !== 'P') {
556 //$('<p>&nbsp;</p>').insertAfter($wrapper);
557 }
558 nextSibling = null;
559 }
560 wrapperParent = null;
561
562 // Check if the url could not be embedded for some reason.
563 if (rawUrl === embeddedContent) {
564 // Echoes the raw url
565 $wrapper.replaceWith($('<p>' + rawUrl + '</p>'));
566 return;
567 }
568
569 $wrapper.removeClass('is-loading');
570
571 // Parse as DOM element
572 var $content;
573 try {
574 $content = $(embeddedContent);
575 } catch (err) {
576 // Fallback to a div, if the result is not a html markup, e.g. a url
577 $content = $('<div>');
578 $content.html(embeddedContent);
579 }
580
581 if (!$('iframe', $content).length && result.data.provider_name!=='Infogram') {
582 var contentWrapper = $($content).clone();
583 contentWrapper.html('');
584
585 $wrapper.removeClass('embedpress_placeholder');
586
587 $wrapper.append(contentWrapper);
588
589 setTimeout(function () {
590 editorInstance.undoManager.transact(function () {
591 var iframe = editorInstance.getDoc().createElement('iframe');
592 iframe.src = tinymce.Env.ie ? 'javascript:""' : '';
593 iframe.frameBorder = '0';
594 iframe.allowTransparency = 'true';
595 iframe.scrolling = 'no';
596 iframe.class = 'wpview-sandbox';
597 iframe.style.width = '100%';
598
599 contentWrapper.append(iframe);
600
601 var iframeWindow = iframe.contentWindow;
602 // Content failed to load.
603 if (!iframeWindow) {
604 return;
605 }
606
607 var iframeDoc = iframeWindow.document;
608
609 $(iframe).load(function () {
610 var maximumChecksAllowed = 8;
611 var checkIndex = 0;
612
613 var checkerInterval = setInterval(function () {
614 if (checkIndex === maximumChecksAllowed) {
615 clearInterval(checkerInterval);
616
617 setTimeout(function () {
618 $wrapper.css('width', iframe.width);
619 $wrapper.css('height', iframe.height);
620 }, 100);
621 } else {
622 if (customAttributes.height) {
623 iframe.height = customAttributes.height;
624 iframe.style.height = customAttributes.height + 'px';
625 } else {
626 iframe.height = $('body', iframeDoc).height();
627 }
628
629 if (customAttributes.width) {
630 iframe.width = customAttributes.width;
631 iframe.style.width = customAttributes.width + 'px';
632 } else {
633 iframe.width = $('body', iframeDoc).width();
634 }
635
636 checkIndex++;
637 }
638 }, 250);
639 });
640
641 iframeDoc.open();
642 iframeDoc.write(
643 '<!DOCTYPE html>' +
644 '<html>' +
645 '<head>' +
646 '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />' +
647 '<style>' +
648 'html {' +
649 'background: transparent;' +
650 'padding: 0;' +
651 'margin: 0;' +
652 '}' +
653 'body#wpview-iframe-sandbox {' +
654 'background: transparent;' +
655 'padding: 1px 0 !important;' +
656 'margin: -1px 0 0 !important;' +
657 '}' +
658 'body#wpview-iframe-sandbox:before,' +
659 'body#wpview-iframe-sandbox:after {' +
660 'display: none;' +
661 'content: "";' +
662 '}' +
663 '</style>' +
664 '</head>' +
665 '<body id="wpview-iframe-sandbox" class="' + editorInstance.getBody().className + '" style="display: inline-block; width: 100%;" >' +
666 $content.html() +
667 '</body>' +
668 '</html>'
669 );
670 iframeDoc.close();
671 });
672 }, 50);
673 } else {
674 $wrapper.removeClass('embedpress_placeholder');
675
676 self.appendElementsIntoWrapper($content, $wrapper, editorInstance);
677 }
678
679 $wrapper.append($('<span class="wpview-end"></span>'));
680
681 if (result && result.data && typeof result.data === 'object') {
682 result.data.width = $($wrapper).width();
683 result.data.height = $($wrapper).height();
684 }
685
686 $(self).triggerHandler('EmbedPress.afterEmbed', {
687 'meta': result.data,
688 'url': rawUrl,
689 'wrapper': $wrapper
690 });
691 });
692 };
693
694 self.appendElementsIntoWrapper = function (elementsList, wrapper, editorInstance) {
695 if (elementsList.length > 0) {
696 $.each(elementsList, function appendElementIntoWrapper (elementIndex, element) {
697 // Check if the element is a script and do not add it now (if added here it wouldn't be executed)
698 if (element.tagName && element.tagName.toLowerCase() !== 'script') {
699 wrapper.append($(element));
700
701 if (element.tagName.toLowerCase() === 'iframe') {
702 $(element).ready(function () {
703 window.setTimeout(function () {
704 $.each(editorInstance.dom.select('div.embedpress_wrapper iframe'), function (elementIndex, iframe) {
705 self.fixIframeSize(iframe);
706 });
707 }, 300);
708 });
709 } else if (element.tagName.toLowerCase() === 'div') {
710 if ($('img', $(element)).length || $('blockquote', wrapper).length) {
711 // This ensures that the embed wrapper have the same width as its content
712 $($(element).parents('.embedpress_wrapper').get(0)).addClass('dynamic-width');
713 }
714
715 $(element).css('max-width', $($(element).parents('body').get(0)).width());
716 }
717 }
718
719 self.loadAsyncDynamicJsCodeFromElement(element, wrapper, editorInstance);
720 });
721 }
722
723 return wrapper;
724 };
725
726 self.encodeEmbedURLSpecialChars = function (content) {
727 if (content.match(SHORTCODE_REGEXP)) {
728 var subject = content.replace(SHORTCODE_REGEXP, '');
729
730 if (!subject.isValidUrl()) {
731 return content;
732 }
733
734 content = subject;
735 subject = null;
736 }
737
738 // Bypass the autolink plugin, avoiding to have the url converted to a link automatically
739 content = content.replace(/http(s?)\:\/\//i, 'embedpress$1://');
740
741 // Bypass the autolink plugin, avoiding to have some urls with @ being treated as email address (e.g. GMaps)
742 content = content.replace('@', '::__at__::').trim();
743
744 return content;
745 };
746
747 self.decodeEmbedURLSpecialChars = function (content, applyShortcode, attributes) {
748 var encodingRegexpRule = /embedpress(s?):\/\//;
749 applyShortcode = (typeof applyShortcode === 'undefined') ? true : applyShortcode;
750 attributes = (typeof attributes === 'undefined') ? {} : attributes;
751
752 var isEncoded = content.match(encodingRegexpRule);
753
754 // Restore http[s] in the url (converted to bypass autolink plugin)
755 content = content.replace(/embedpress(s?):\/\//, 'http$1://');
756 content = content.replace('::__at__::', '@').trim();
757
758 if ('class' in attributes) {
759 var classesList = attributes.class.split(/\s/g);
760 var shouldRemoveDynamicWidthClass = false;
761 for (var classIndex = 0; classIndex < classesList.length; classIndex++) {
762 if (classesList[classIndex] === 'dynamic-width') {
763 shouldRemoveDynamicWidthClass = classIndex;
764 break;
765 }
766 }
767
768 if (shouldRemoveDynamicWidthClass !== false) {
769 classesList.splice(shouldRemoveDynamicWidthClass, 1);
770
771 if (classesList.length === 0) {
772 delete attributes.class;
773 }
774
775 attributes.class = classesList.join(' ');
776 }
777
778 shouldRemoveDynamicWidthClass = classesList = classIndex = null;
779 }
780
781 if (isEncoded && applyShortcode) {
782 var shortcode = '[' + $data.EMBEDPRESS_SHORTCODE;
783 if (!!Object.keys(attributes).length) {
784 var attrValue;
785
786 for (var attrName in attributes) {
787 attrValue = attributes[attrName];
788
789 // Prevent `class` property being empty an treated as a boolean param
790 if (attrName.toLowerCase() === 'class' && !attrValue.length) {
791
792 } else {
793 if (attrValue.isBoolean()) {
794 shortcode += ' ';
795 if (attrValue.isFalse()) {
796 shortcode += '!';
797 }
798
799 shortcode += attrName;
800 } else {
801 shortcode += ' ' + attrName + '="' + attrValue + '"';
802 }
803 }
804 }
805 attrValue = attrName = null;
806 }
807
808 content = shortcode + ']' + content + '[/' + $data.EMBEDPRESS_SHORTCODE + ']';
809 }
810
811 return content;
812 };
813
814 /**
815 * Method executed after find the editor. It will make additional
816 * configurations and add the content's stylesheets for the preview
817 *
818 * @return void
819 */
820 self.onFindEditor = function (editorInstance) {
821 self.each = tinymce.each;
822 self.extend = tinymce.extend;
823 self.JSON = tinymce.util.JSON;
824 self.Node = tinymce.html.Node;
825
826 function onFindEditorCallback () {
827 $(window.document.getElementsByTagName('head')[0]).append($('<link rel="stylesheet" type="text/css" href="' + (PLG_SYSTEM_ASSETS_CSS_PATH + '/vendor/bootstrap/bootstrap.min.css?v=' + self.params.versionUID) + '">'));
828
829 self.addStylesheet(PLG_SYSTEM_ASSETS_CSS_PATH + '/font.css?v=' + self.params.versionUID, editorInstance, editorInstance);
830 self.addStylesheet(PLG_SYSTEM_ASSETS_CSS_PATH + '/preview.css?v=' + self.params.versionUID, editorInstance, editorInstance);
831 self.addStylesheet(PLG_CONTENT_ASSETS_CSS_PATH + '/embedpress.css?v=' + self.params.versionUID, editorInstance, editorInstance);
832 self.addEvent('nodechange', editorInstance, self.onNodeChange);
833 self.addEvent('keydown', editorInstance, function (e) {
834 self.onKeyDown(e, editorInstance);
835 });
836
837 var onUndoCallback = function (e) {
838 self.onUndo(e, editorInstance);
839 };
840
841 self.addEvent('undo', editorInstance, onUndoCallback); // TinyMCE
842 self.addEvent('undo', editorInstance.undoManager, onUndoCallback); // JCE
843
844 var doc = editorInstance.getDoc();
845 $(doc).on('mouseenter', '.embedpress_wrapper', function (e) {
846 self.onMouseEnter(e, editorInstance);
847 });
848 $(doc).on('mouseout', '.embedpress_wrapper', self.onMouseOut);
849 $(doc).on('mousedown', '.embedpress_wrapper > .embedpress_controller_panel', function (e) {
850 self.cancelEvent(e, editorInstance);
851 });
852 doc = null;
853
854 // Add the node filter that will convert the url into the preview box for the embed code
855 editorInstance.parser.addNodeFilter('#text', function addNodeFilterIntoParser (nodes, arg) {
856 self.each(nodes, function eachNodeInParser (node) {
857 // Stop if the node is "isolated". It would generate an error in the browser console and break.
858 if (node.parent === null && node.prev === null) {
859 return;
860 }
861
862 var subject = node.value.trim();
863
864 if (!subject.isValidUrl()) {
865 if (!subject.match(SHORTCODE_REGEXP)) {
866 return;
867 }
868 }
869 subject = self.decodeEmbedURLSpecialChars(subject);
870 if (!subject.isValidUrl()) {
871 if (!subject.match(SHORTCODE_REGEXP)) {
872 return;
873 }
874 }
875
876 subject = node.value.stripShortcode($data.EMBEDPRESS_SHORTCODE).trim();
877
878 // These patterns need to have groups for the pre and post texts
879 // @TODO: maybe remove this list of URLs? Let the server side code decide what URL should be parsed
880 var patterns = self.getProvidersURLPatterns();
881
882 (function tryToMatchContentAgainstUrlPatternWithIndex (urlPatternIndex) {
883 if (urlPatternIndex < patterns.length) {
884 var urlPattern = patterns[urlPatternIndex];
885 var urlPatternRegex = new RegExp(urlPattern);
886
887 var url = self.decodeEmbedURLSpecialChars(subject).trim();
888
889 var matches = url.match(urlPatternRegex);
890 // Check if content matches the url pattern.
891 if (matches && matches !== null && !!matches.length) {
892 url = self.encodeEmbedURLSpecialChars(matches[2]);
893
894 var wrapper = self.addURLsPlaceholder(node, url, editorInstance);
895
896 setTimeout(function () {
897 var doc = editorInstance.getDoc();
898
899 if (doc === null) {
900 return;
901 }
902
903 var previewWrapper = $(doc.querySelector('#' + wrapper.attributes.map['id']));
904 var previewWrapperParent = $(previewWrapper.parent());
905
906 if (previewWrapperParent && previewWrapperParent.prop('tagName') && previewWrapperParent.prop('tagName').toUpperCase() === 'P') {
907 previewWrapperParent.replaceWith(previewWrapper);
908 }
909
910 var previewWrapperOlderSibling = previewWrapper.prev();
911 if (previewWrapperOlderSibling && previewWrapperOlderSibling.prop('tagName') && previewWrapperOlderSibling.prop('tagName').toUpperCase() === 'P' && !previewWrapperOlderSibling.html().replace(/\&nbsp\;/i, '').length) {
912 previewWrapperOlderSibling.remove();
913 } else {
914 if (typeof previewWrapperOlderSibling.html() !== 'undefined') {
915 if (previewWrapperOlderSibling.html().match(/<[\/]?br>/)) {
916 if (!previewWrapperOlderSibling.prev().length) {
917 previewWrapperOlderSibling.remove();
918 }
919 }
920 }
921 }
922
923 var previewWrapperYoungerSibling = previewWrapper.next();
924 if (previewWrapperYoungerSibling && previewWrapperYoungerSibling.length && previewWrapperYoungerSibling.prop('tagName').toUpperCase() === 'P') {
925 if (!previewWrapperYoungerSibling.next().length && !previewWrapperYoungerSibling.html().replace(/\&nbsp\;/i, '').length) {
926 previewWrapperYoungerSibling.remove();
927 $('<p>&nbsp;</p>').insertAfter(previewWrapper);
928 }
929 } else {
930 $('<p>&nbsp;</p>').insertAfter(previewWrapper);
931 }
932
933 setTimeout(function () {
934 editorInstance.selection.select(editorInstance.getBody(), true);
935 editorInstance.selection.collapse(false);
936 }, 50);
937 }, 50);
938 } else {
939 // No match. So we move on to check the next url pattern.
940 tryToMatchContentAgainstUrlPatternWithIndex(urlPatternIndex + 1);
941 }
942 }
943 })(0);
944 });
945 });
946
947 // Add the filter that will convert the preview box/embed code back to the raw url
948 editorInstance.serializer.addNodeFilter('div', function addNodeFilterIntoSerializer (nodes, arg) {
949 self.each(nodes, function eachNodeInSerializer (node) {
950 // Stop if the node is "isolated". It would generate an error in the browser console and break.
951 if (node.parent === null && node.prev === null) {
952 return;
953 }
954
955 var nodeClasses = (node.attributes.map.class || '').split(' ');
956 var wrapperFactoryClasses = ['embedpress_wrapper', 'embedpress_placeholder', 'wpview', 'wpview-wrap'];
957
958 var isWrapped = nodeClasses.filter(function (n) {
959 return wrapperFactoryClasses.indexOf(n) != -1;
960 }).length > 0;
961
962 if (isWrapped) {
963 var factoryAttributes = ['id', 'style', 'data-loading-text', 'data-uid', 'data-url'];
964 var customAttributes = {};
965 var dataPrefix = 'data-';
966 for (var attr in node.attributes.map) {
967 if (attr.toLowerCase() !== 'class') {
968 if (factoryAttributes.indexOf(attr) < 0) {
969 // Remove the "data-" prefix for more readability
970 customAttributes[attr.replace(dataPrefix, '')] = node.attributes.map[attr];
971 }
972 } else {
973 var customClasses = [];
974 for (var wrapperClassIndex in nodeClasses) {
975 var wrapperClass = nodeClasses[wrapperClassIndex];
976 if (wrapperFactoryClasses.indexOf(wrapperClass) === -1) {
977 customClasses.push(wrapperClass);
978 }
979 }
980
981 if (!!customClasses.length) {
982 customAttributes.class = customClasses.join(' ');
983 }
984 }
985 }
986
987 var p = new self.Node('p', 1);
988
989 var text = new self.Node('#text', 3);
990 text.value = (node.attributes.map && typeof node.attributes.map['data-url'] != 'undefined') ? self.decodeEmbedURLSpecialChars(node.attributes.map['data-url'].trim(), true, customAttributes) : '';
991
992 p.append(text.clone());
993
994 node.replace(text);
995 text.replace(p);
996 }
997 });
998 });
999
1000 editorInstance.serializer.addNodeFilter('p', function addNodeFilterIntoSerializer (nodes, arg) {
1001 self.each(nodes, function eachNodeInSerializer (node) {
1002 if (node.firstChild == node.lastChild) {
1003 if (node.firstChild && 'value' in node.firstChild && (node.firstChild.value === '&nbsp;' || !node.firstChild.value.trim().length)) {
1004 node.remove();
1005 }
1006 }
1007 });
1008 });
1009
1010 //@todo:isthiseachreallynecessary?
1011 // Add event to reconfigure wrappers every time the content is loaded
1012 tinymce.each(tinymce.editors, function onEachEditor (editor) {
1013 self.addEvent('loadContent', editor, function onInitEditor (ed) {
1014 self.configureWrappers(editor);
1015 });
1016 });
1017
1018 // Add the edit form
1019
1020 // @todo: This is needed only for JCE, to fix the img placeholder. Try to find out a better approach to avoid the placeholder blink
1021 window.setTimeout(
1022 function afterTimeoutOnFindEditor () {
1023 /*
1024 * This is required because after load/refresh the page, the
1025 * onLoadContent is not being triggered automatically, so
1026 * we force the event
1027 */
1028 editorInstance.load();
1029 },
1030 // If in JCE the user see the placeholder (img) instead of the iframe after load/refresh the pagr, this time is too short
1031 500
1032 );
1033 }
1034
1035 // Let's make sure the inner doc has been fully loaded first.
1036 var checkTimesLimit = 100;
1037 var checkIndex = 0;
1038 var statusCheckerInterval = setInterval(function () {
1039 if (checkIndex === checkTimesLimit) {
1040 clearInterval(statusCheckerInterval);
1041 alert('For some reason TinyMCE was not fully loaded yet. Please, refresh the page and try again.');
1042 } else {
1043 var doc = editorInstance.getDoc();
1044 if (doc) {
1045 clearInterval(statusCheckerInterval);
1046 onFindEditorCallback();
1047 } else {
1048 checkIndex++;
1049 }
1050 }
1051 }, 250);
1052 };
1053
1054 self.fixIframeSize = function (iframe) {
1055 var maxWidth = 480;
1056 if ($(iframe).width() > maxWidth && !$(iframe).data('size-fixed')) {
1057 var ratio = $(iframe).height() / $(iframe).width();
1058 $(iframe).width(maxWidth);
1059 $(iframe).height(maxWidth * ratio);
1060 $(iframe).css('max-width', maxWidth);
1061 $(iframe).attr('max-width', maxWidth);
1062
1063 $(iframe).data('size-fixed', true);
1064 }
1065 };
1066
1067 /**
1068 * Function triggered on mouse enter the wrapper
1069 *
1070 * @param object e The event
1071 * @return void
1072 */
1073 self.onMouseEnter = function (e, editorInstance) {
1074 self.displayPreviewControllerPanel($(e.currentTarget), editorInstance);
1075 };
1076
1077 /**
1078 * Function triggered on mouse get out of the wrapper
1079 *
1080 * @param object e The event
1081 * @return void
1082 */
1083 self.onMouseOut = function (e) {
1084 // Check if the destiny is not a child element
1085 // Chrome
1086 if (self.isDefined(e.toElement)) {
1087 if (e.toElement.parentElement == e.fromElement
1088 || $(e.toElement).hasClass('embedpress_ignore_mouseout')
1089 ) {
1090 return false;
1091 }
1092 }
1093
1094 // Firefox
1095 if (self.isDefined(e.relatedTarget)) {
1096 if ($(e.relatedTarget).hasClass('embedpress_ignore_mouseout')) {
1097 return false;
1098 }
1099 }
1100
1101 self.hidePreviewControllerPanel();
1102 };
1103
1104 /**
1105 * Callback triggered by paste events. This should be hooked by TinyMCE's paste_preprocess
1106 * setting. A normal bind to the onPaste event doesn't work correctly all the times
1107 * (specially when you copy and paste content from the same editor).
1108 *
1109 * @param mixed - plugin
1110 * @param mixed - args
1111 *
1112 * @return void
1113 */
1114
1115 self.onPaste = function (plugin, args) {
1116 var urlPatternRegex = new RegExp(/(https?):\/\/([w]{3}\.)?.+?(?:\s|$)/i);
1117 var urlPatternsList = self.getProvidersURLPatterns();
1118
1119 // Split the pasted content into separated lines.
1120 var contentLines = args.content.split(/\n/g) || [];
1121 contentLines = contentLines.map(function (line, itemIndex) {
1122 // Check if there's a url into `line`.
1123 if (line.match(urlPatternRegex)) {
1124 // Split the current line across its space-characters to isolate the url.
1125 let termsList = line.trim().split(/\s+/);
1126 termsList = termsList.map(function (term, termIndex) {
1127 // Check if the term into the current line is a url.
1128 var match = term.match(urlPatternRegex);
1129 if (match) {
1130 for (var urlPatternIndex = 0; urlPatternIndex < urlPatternsList.length; urlPatternIndex++) {
1131 // Isolates that url from the rest of the content if the service is supported.
1132 var urlPattern = new RegExp(urlPatternsList[urlPatternIndex]);
1133 if (urlPattern.test(term)) {
1134 return '</p><p>' + match[0] + '</p><p>';
1135 }
1136 }
1137 }
1138
1139 return term;
1140 });
1141
1142 termsList[termsList.length - 1] = termsList[termsList.length - 1] + '<br>';
1143
1144 line = termsList.join(' ');
1145 }
1146
1147 return line;
1148 });
1149
1150 // Check if the text was transformed or not. If it was, add wrappers
1151 var content = contentLines.join('');
1152
1153 if (content.replace(/<br>$/, '') !== args.content) {
1154 args.content = '<p>' + args.content + '</p>';
1155 }
1156 };
1157
1158 /**
1159 * Method trigered on every node change, to detect new lines. It will
1160 * try to fix a default behavior for some editors of clone the parent
1161 * element when adding a line break. This will clone the embed wrapper
1162 * if we set the cursor after a preview wrapper and hit enter.
1163 *
1164 * @param object e The event
1165 * @return void
1166 */
1167 self.onNodeChange = function (e) {
1168 // Fix the clone parent on break lines issue
1169 // Check if a line break was added
1170 if (e.element.tagName === 'BR') {
1171 // Check one of the parent elements is a clonned embed wrapper
1172 if (e.parents.length > 0) {
1173 $.each(e.parents, function (index, parent) {
1174 if ($(parent).hasClass('embedpress_wrapper')) {
1175 // Remove the cloned wrapper and replace with a 'br' tag
1176 $(parent).replaceWith($('<br>'));
1177 }
1178 });
1179 }
1180 } else if (e.element.tagName === 'IFRAME') {
1181 if (e.parents.length > 0) {
1182 $.each(e.parents, function (index, parent) {
1183 parent = $(parent);
1184 if (parent.hasClass('embedpress_wrapper')) {
1185 var wrapper = $('.embedpress-wrapper', parent);
1186 if (wrapper.length > 1) {
1187 wrapper.get(0).remove();
1188 }
1189 }
1190 });
1191 }
1192 }
1193 };
1194
1195 self.onKeyDown = function (e, editorInstance) {
1196 var node = editorInstance.selection.getNode();
1197
1198 if (e.keyCode == 8 || e.keyCode == 46) {
1199 if (node.nodeName.toLowerCase() === 'p') {
1200 var children = $(node).children();
1201 if (children.length > 0) {
1202 $.each(children, function () {
1203 // On delete, make sure to remove the wrapper and children, not only the wrapper
1204 if ($(this).hasClass('embedpress_wrapper') || $(this).hasClass('embedpress_ignore_mouseout')) {
1205 $(this).remove();
1206
1207 editorInstance.focus();
1208 }
1209 });
1210 }
1211 }
1212 } else {
1213 // Ignore the arrows keys
1214 var arrowsKeyCodes = [37, 38, 39, 40];
1215 if (arrowsKeyCodes.indexOf(e.keyCode) == -1) {
1216
1217 // Check if we are inside a preview wrapper
1218 if ($(node).hasClass('embedpress_wrapper') || $(node).hasClass('embedpress_ignore_mouseout')) {
1219 // Avoid delete the wrapper or block line break if we are inside the wrapper
1220 if (e.keyCode == 13) {
1221 wrapper = $(self.getWrapperFromChild(node));
1222 if (wrapper.length > 0) {
1223 // Creates a temporary element which will be inserted after the wrapper
1224 var tmpId = '__embedpress__tmp_' + self.makeId();
1225 wrapper.after($('<span id="' + tmpId + '"></span>'));
1226 // Get and select the temporary element
1227 var span = editorInstance.dom.select('span#' + tmpId)[0];
1228 editorInstance.selection.select(span);
1229 // Remove the temporary element
1230 $(span).remove();
1231 }
1232
1233 return true;
1234 } else {
1235 // If we are inside the embed preview, ignore any key to avoid edition
1236 return self.cancelEvent(e, editorInstance);
1237 }
1238 }
1239 }
1240 }
1241
1242 return true;
1243 };
1244
1245 self.getWrapperFromChild = function (element) {
1246 // Is the wrapper
1247 if ($(element).hasClass('embedpress_wrapper')) {
1248 return element;
1249 } else {
1250 var $parent = $(element).parent();
1251
1252 if ($parent.length > 0) {
1253 return self.getWrapperFromChild($parent[0]);
1254 }
1255 }
1256
1257 return false;
1258 };
1259
1260 self.onUndo = function (e, editorInstance) {
1261 // Force re-render everything
1262 editorInstance.load();
1263 };
1264
1265 self.cancelEvent = function (e, editorInstance) {
1266 e.preventDefault();
1267 e.stopPropagation();
1268 editorInstance.dom.events.cancel();
1269
1270 return false;
1271 };
1272
1273 /**
1274 * Method executed when the edit button is clicked. It will display
1275 * a field with the current url, to update the current embed's source
1276 * url.
1277 *
1278 * @param Object e The event
1279 * @return void
1280 */
1281 self.onClickEditButton = function (e, editorInstance) {
1282 // Prevent edition of the panel
1283 self.cancelEvent(e, editorInstance);
1284
1285 self.activeWrapperForModal = self.activeWrapper;
1286
1287 var $wrapper = self.activeWrapperForModal;
1288 var wrapperUid = $wrapper.prop('id').replace('embedpress_wrapper_', '');
1289
1290 var customAttributes = {};
1291
1292 var $embedInnerWrapper = $('.embedpress-wrapper', $wrapper);
1293 var embedItem = $('iframe', $wrapper);
1294 if (!embedItem.length) {
1295 embedItem = null;
1296 }
1297 if ($embedInnerWrapper.length > 1){
1298 $.each($embedInnerWrapper[0].attributes, function () {
1299 if (this.specified) {
1300 if (this.name !== 'class') {
1301 customAttributes[this.name.replace('data-', '').toLowerCase()] = this.value;
1302 }
1303 }
1304 });
1305 }
1306
1307
1308 var embedWidth = (((embedItem && embedItem.width()) || $embedInnerWrapper.data('width')) || $embedInnerWrapper.width()) || '';
1309 var embedHeight = (((embedItem && embedItem.height()) || $embedInnerWrapper.data('height')) || $embedInnerWrapper.height()) || '';
1310
1311 embedItem = $embedInnerWrapper = null;
1312
1313 $('<div class="loader-indicator"><i class="embedpress-icon-reload"></i></div>').appendTo($wrapper);
1314
1315 setTimeout(function () {
1316 $.ajax({
1317 type: 'GET',
1318 url: self.params.baseUrl + 'wp-admin/admin-ajax.php',
1319 data: {
1320 action: 'embedpress_get_embed_url_info',
1321 url: self.decodeEmbedURLSpecialChars($wrapper.data('url'), false)
1322 },
1323 beforeSend: function (request, requestSettings) {
1324 $('.loader-indicator', $wrapper).addClass('is-loading');
1325 },
1326 success: function (response) {
1327 if (!response) {
1328 bootbox.alert('Unable to get a valid response from the server.');
1329 return;
1330 }
1331 if (response.canBeResponsive) {
1332 var embedShouldBeResponsive = true;
1333 if ('width' in customAttributes || 'height' in customAttributes) {
1334 embedShouldBeResponsive = false;
1335 } else if ('responsive' in customAttributes && customAttributes['responsive'].isFalse()) {
1336 embedShouldBeResponsive = false;
1337 }
1338 }
1339
1340 bootbox.dialog({
1341 className: 'embedpress-modal',
1342 title: 'Editing Embed properties',
1343 message: '<form id="form-' + wrapperUid + '" embedpress>' +
1344 '<div class="row">' +
1345 '<div class="col-md-12">' +
1346 '<div class="form-group">' +
1347 '<label for="input-url-' + wrapperUid + '">Url</label>' +
1348 '<input class="form-control" type="url" id="input-url-' + wrapperUid + '" value="' + self.decodeEmbedURLSpecialChars($wrapper.data('url'), false) + '">' +
1349 '</div>' +
1350 '</div>' +
1351 '</div>' +
1352 '<div class="row">' +
1353 (response.canBeResponsive ?
1354 '<div class="col-md-12">' +
1355 '<label>Responsive</label>' +
1356 '<div class="form-group">' +
1357 '<label class="radio-inline">' +
1358 '<input type="radio" name="input-responsive-' + wrapperUid + '" id="input-responsive-1-' + wrapperUid + '" value="1"' + (embedShouldBeResponsive ? ' checked="checked"' : '') + '> Yes' +
1359 '</label>' +
1360 '<label class="radio-inline">' +
1361 '<input type="radio" name="input-responsive-' + wrapperUid + '" id="input-responsive-0-' + wrapperUid + '" value="0"' + (!embedShouldBeResponsive ? ' checked="checked"' : '') + '> No' +
1362 '</label>' +
1363 '</div>' +
1364 '</div>' : '') +
1365 '<div class="col-md-6">' +
1366 '<div class="form-group">' +
1367 '<label for="input-width-' + wrapperUid + '">Width</label>' +
1368 '<input class="form-control" type="integer" id="input-width-' + wrapperUid + '" value="' + embedWidth + '"' + (embedShouldBeResponsive ? ' disabled' : '') + '>' +
1369 '</div>' +
1370 '</div>' +
1371 '<div class="col-md-6">' +
1372 '<div class="form-group">' +
1373 '<label for="input-height-' + wrapperUid + '">Height</label>' +
1374 '<input class="form-control" type="integer" id="input-height-' + wrapperUid + '" value="' + embedHeight + '"' + (embedShouldBeResponsive ? ' disabled' : '') + '>' +
1375 '</div>' +
1376 '</div>' +
1377 '</div>' +
1378 '</form>',
1379 buttons: {
1380 danger: {
1381 label: 'Cancel',
1382 className: 'btn-default',
1383 callback: function () {
1384 // do nothing
1385 self.activeWrapperForModal = null;
1386 }
1387 },
1388 success: {
1389 label: 'Save',
1390 className: 'btn-primary',
1391 callback: function () {
1392 var $wrapper = self.activeWrapperForModal;
1393
1394 // Select the current wrapper as a base for the new element
1395 editorInstance.focus();
1396 editorInstance.selection.select($wrapper[0]);
1397
1398 $wrapper.children().remove();
1399 $wrapper.remove();
1400
1401 if (response.canBeResponsive) {
1402 if ($('#form-' + wrapperUid + ' input[name="input-responsive-' + wrapperUid + '"]:checked').val().isFalse()) {
1403 var embedCustomWidth = $('#input-width-' + wrapperUid).val();
1404 if (parseInt(embedCustomWidth) > 0) {
1405 customAttributes['width'] = embedCustomWidth;
1406 }
1407
1408 var embedCustomHeight = $('#input-height-' + wrapperUid).val();
1409 if (parseInt(embedCustomHeight) > 0) {
1410 customAttributes['height'] = embedCustomHeight;
1411 }
1412
1413 customAttributes['responsive'] = 'false';
1414 } else {
1415 delete customAttributes['width'];
1416 delete customAttributes['height'];
1417
1418 customAttributes['responsive'] = 'true';
1419 }
1420 } else {
1421 var embedCustomWidth = $('#input-width-' + wrapperUid).val();
1422 if (parseInt(embedCustomWidth) > 0) {
1423 customAttributes['width'] = embedCustomWidth;
1424 }
1425
1426 var embedCustomHeight = $('#input-height-' + wrapperUid).val();
1427 if (parseInt(embedCustomHeight) > 0) {
1428 customAttributes['height'] = embedCustomHeight;
1429 }
1430 }
1431
1432 var customAttributesList = [];
1433 if (!!Object.keys(customAttributes).length) {
1434 for (var attrName in customAttributes) {
1435 customAttributesList.push(attrName + '="' + customAttributes[attrName] + '"');
1436 }
1437 }
1438
1439 var shortcode = '[' + $data.EMBEDPRESS_SHORTCODE + (customAttributesList.length > 0 ? ' ' + customAttributesList.join(' ') : '') + ']' + $('#input-url-' + wrapperUid).val() + '[/' + $data.EMBEDPRESS_SHORTCODE + ']';
1440 // We do not directly replace the node because it was causing a bug on a second edit attempt
1441 editorInstance.execCommand('mceInsertContent', false, shortcode);
1442
1443 self.configureWrappers(editorInstance);
1444 }
1445 }
1446 }
1447 });
1448
1449 $('form[embedpress]').on('change', 'input[type="radio"]', function (e) {
1450 var self = $(this);
1451 var form = self.parents('form[embedpress]');
1452
1453 $('input[type="integer"]', form).prop('disabled', self.val().isTrue());
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($data.previewSettings);
1656 });
1657 })(jQuery, String, $data);
1658