PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents / 2.1.6
EmbedPress – PDF Embedder, 3D PDF FlipBook, Google Reviews, YouTube Videos, Upload & Embed PDF documents v2.1.6
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
vendor 9 years ago index.html 9 years ago preview.js 7 years ago settings.js 8 years ago
preview.js
1661 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 attr in attributes) {
52 parsedAttributes.push(attr + '="' + attributes[attr] + '"');
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 {
241 subject = $(subject);
242 if (subject.prop('tagName').toLowerCase() === "script") {
243 var scriptSrc = subject.attr('src') || null;
244 if (!scriptSrc) {
245 self.addScriptDeclaration(wrapper, subject.html(), editorInstance);
246 } else {
247 self.addScript(scriptSrc, null, wrapper, editorInstance);
248 }
249 } else {
250 var innerScriptsList = $('script', subject);
251 if (innerScriptsList.length > 0) {
252 $.each(innerScriptsList, function(innerScriptIndex, innerScript) {
253 self.loadAsyncDynamicJsCodeFromElement(innerScript, wrapper, editorInstance);
254 });
255 }
256 }
257 }
258
259 /**
260 * Method executed on the document ready event
261 *
262 * @return void
263 */
264 self.onReady = function() {
265 var findEditors = function() {
266 // Wait until the editor is available
267 var interval = window.setInterval(
268 function() {
269 var editorsFound = self.getEditors();
270 if (editorsFound.length) {
271 self.loadedEditors = editorsFound;
272
273 for (var editorIndex = 0; editorIndex < self.loadedEditors.length; editorIndex++) {
274 self.onFindEditor(self.loadedEditors[editorIndex]);
275 }
276
277 window.clearInterval(interval);
278
279 return self.loadedEditors;
280 }
281 },
282 250
283 );
284 };
285
286 if (self.tinymceIsAvailable()) {
287 findEditors();
288 }
289
290 // Add support for the Beaver Builder.
291 if (typeof FLLightbox !== 'undefined') {
292 $.each(FLLightbox._instances, function(index) {
293 FLLightbox._instances[index].on('open', function() {
294 setTimeout(function() {
295 findEditors();
296 }, 500);
297 });
298
299 FLLightbox._instances[index].on('didHideLightbox', function() {
300 setTimeout(function() {
301 findEditors();
302 }, 500);
303 });
304 });
305 }
306 };
307
308 /**
309 * Detects if tinymce object is available
310 * @return Boolean True, if available
311 */
312 self.tinymceIsAvailable = function() {
313 return typeof window.tinymce === 'object' || typeof window.tinyMCE === "object";
314 }
315
316 /**
317 * Returns true if the controller panel is active
318 * @return Boolean True, if the controller panel is active
319 */
320 self.controllerPanelIsActive = function() {
321 return typeof self.activeControllerPanel !== 'undefined' && self.activeControllerPanel !== null;
322 };
323
324 /**
325 * Returns the editor
326 * @return Object The editor
327 */
328 self.getEditors = function() {
329 if (!window.tinymce || !window.tinymce.editors || window.tinymce.editors.length === 0) {
330 return [];
331 }
332
333 return window.tinymce.editors || [];
334 };
335
336 /**
337 * Parses the content, sending it to the component which will
338 * look for urls to be parsed into embed codes
339 *
340 * @param string content The content
341 * @param function onsuccess The callback called on success
342 * @return void
343 */
344 self.getParsedContent = function(content, onsuccess) {
345 // Get the parsed content
346 $.ajax({
347 type: 'POST',
348 url: self.params.baseUrl +"wp-admin/admin-ajax.php",
349 data: {
350 action: "embedpress_do_ajax_request",
351 subject: content
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($data.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($data.EMBEDPRESS_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($data.EMBEDPRESS_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($data.EMBEDPRESS_SHORTCODE);
548
549 var $wrapper = $(self.getElementInContentById('embedpress_wrapper_' + uid, editorInstance));
550 var wrapperParent = $($wrapper.parent());
551
552 // Check if $wrapper was rendered inside a <p> element.
553 if (wrapperParent.prop('tagName') && wrapperParent.prop('tagName').toUpperCase() === "P") {
554 wrapperParent.replaceWith($wrapper);
555 // Check if there's at least one "space" after $wrapper.
556 var nextSibling = $($wrapper).next();
557 if (!nextSibling.length || nextSibling.prop('tagName').toUpperCase() !== "P") {
558 //$('<p>&nbsp;</p>').insertAfter($wrapper);
559 }
560 nextSibling = null;
561 }
562 wrapperParent = null;
563
564 // Check if the url could not be embedded for some reason.
565 if (rawUrl === embeddedContent) {
566 // Echoes the raw url
567 $wrapper.replaceWith($('<p>'+ rawUrl +'</p>'));
568 return;
569 }
570
571 $wrapper.removeClass('is-loading');
572
573 // Parse as DOM element
574 var $content;
575 try {
576 $content = $(embeddedContent);
577 } catch(err) {
578 // Fallback to a div, if the result is not a html markup, e.g. a url
579 $content = $('<div>');
580 $content.html(embeddedContent);
581 }
582
583 if (!$('iframe', $content).length) {
584 var contentWrapper = $($content).clone();
585 contentWrapper.html('');
586
587 $wrapper.removeClass('embedpress_placeholder');
588
589 $wrapper.append(contentWrapper);
590
591 setTimeout(function() {
592 editorInstance.undoManager.transact(function() {
593 var iframe = editorInstance.getDoc().createElement('iframe');
594 iframe.src = tinymce.Env.ie ? 'javascript:""' : '';
595 iframe.frameBorder = '0';
596 iframe.allowTransparency = 'true';
597 iframe.scrolling = 'no';
598 iframe.class = "wpview-sandbox";
599 iframe.style.width = '100%';
600
601 contentWrapper.append(iframe);
602
603 var iframeWindow = iframe.contentWindow;
604 // Content failed to load.
605 if (!iframeWindow) {
606 return;
607 }
608
609 var iframeDoc = iframeWindow.document;
610
611 $(iframe).load(function() {
612 var maximumChecksAllowed = 8;
613 var checkIndex = 0;
614
615 var checkerInterval = setInterval(function() {
616 if (checkIndex === maximumChecksAllowed) {
617 clearInterval(checkerInterval);
618
619 setTimeout(function() {
620 $wrapper.css('width', iframe.width);
621 $wrapper.css('height', iframe.height);
622 }, 100);
623 } else {
624 if (customAttributes.height) {
625 iframe.height = customAttributes.height;
626 iframe.style.height = customAttributes.height +'px';
627 } else {
628 iframe.height = $('body', iframeDoc).height();
629 }
630
631 if (customAttributes.width) {
632 iframe.width = customAttributes.width;
633 iframe.style.width = customAttributes.width +'px';
634 } else {
635 iframe.width = $('body', iframeDoc).width();
636 }
637
638 checkIndex++;
639 }
640 }, 250);
641 });
642
643 iframeDoc.open();
644 iframeDoc.write(
645 '<!DOCTYPE html>'+
646 '<html>'+
647 '<head>'+
648 '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />'+
649 '<style>'+
650 'html {'+
651 'background: transparent;'+
652 'padding: 0;'+
653 'margin: 0;'+
654 '}'+
655 'body#wpview-iframe-sandbox {'+
656 'background: transparent;'+
657 'padding: 1px 0 !important;'+
658 'margin: -1px 0 0 !important;'+
659 '}'+
660 'body#wpview-iframe-sandbox:before,'+
661 'body#wpview-iframe-sandbox:after {'+
662 'display: none;'+
663 'content: "";'+
664 '}'+
665 '</style>'+
666 '</head>'+
667 '<body id="wpview-iframe-sandbox" class="'+ editorInstance.getBody().className +'" style="display: inline-block;">'+
668 $content.html() +
669 '</body>'+
670 '</html>'
671 );
672 iframeDoc.close();
673 });
674 }, 50);
675 } else {
676 $wrapper.removeClass('embedpress_placeholder');
677
678 self.appendElementsIntoWrapper($content, $wrapper, editorInstance);
679 }
680
681 $wrapper.append($('<span class="wpview-end"></span>'));
682
683 if (result && result.data && typeof result.data === "object") {
684 result.data.width = $($wrapper).width();
685 result.data.height = $($wrapper).height();
686 }
687
688 $(self).triggerHandler('EmbedPress.afterEmbed', {
689 'meta' : result.data,
690 'url' : rawUrl,
691 'wrapper': $wrapper
692 });
693 });
694 };
695
696 self.appendElementsIntoWrapper = function(elementsList, wrapper, editorInstance) {
697 if (elementsList.length > 0) {
698 $.each(elementsList, function appendElementIntoWrapper(elementIndex, element) {
699 // Check if the element is a script and do not add it now (if added here it wouldn't be executed)
700 if (element.tagName.toLowerCase() !== 'script') {
701 wrapper.append($(element));
702
703 if (element.tagName.toLowerCase() === 'iframe') {
704 $(element).ready(function() {
705 window.setTimeout(function() {
706 $.each(editorInstance.dom.select('div.embedpress_wrapper iframe'), function(elementIndex, iframe) {
707 self.fixIframeSize(iframe);
708 });
709 }, 300);
710 });
711 } else if (element.tagName.toLowerCase() === "div") {
712 if ($('img', $(element)).length || $('blockquote', wrapper).length) {
713 // This ensures that the embed wrapper have the same width as its content
714 $($(element).parents('.embedpress_wrapper').get(0)).addClass('dynamic-width');
715 }
716
717 $(element).css('max-width', $($(element).parents('body').get(0)).width());
718 }
719 }
720
721 self.loadAsyncDynamicJsCodeFromElement(element, wrapper, editorInstance);
722 });
723 }
724
725 return wrapper;
726 };
727
728 self.encodeEmbedURLSpecialChars = function(content) {
729 if (content.match(SHORTCODE_REGEXP)) {
730 var subject = content.replace(SHORTCODE_REGEXP, '');
731
732 if (!subject.isValidUrl()) {
733 return content;
734 }
735
736 content = subject;
737 subject = null;
738 }
739
740 // Bypass the autolink plugin, avoiding to have the url converted to a link automatically
741 content = content.replace(/http(s?)\:\/\//i, 'embedpress$1://');
742
743 // Bypass the autolink plugin, avoiding to have some urls with @ being treated as email address (e.g. GMaps)
744 content = content.replace('@', '::__at__::').trim();
745
746 return content;
747 };
748
749 self.decodeEmbedURLSpecialChars = function(content, applyShortcode, attributes) {
750 var encodingRegexpRule = /embedpress(s?):\/\//;
751 applyShortcode = (typeof applyShortcode === "undefined") ? true : applyShortcode;
752 attributes = (typeof attributes === "undefined") ? {} : attributes;
753
754 var isEncoded = content.match(encodingRegexpRule);
755
756 // Restore http[s] in the url (converted to bypass autolink plugin)
757 content = content.replace(/embedpress(s?):\/\//, 'http$1://');
758 content = content.replace('::__at__::', '@').trim();
759
760 if ("class" in attributes) {
761 var classesList = attributes.class.split(/\s/g);
762 var shouldRemoveDynamicWidthClass = false;
763 for (var classIndex = 0; classIndex < classesList.length; classIndex++) {
764 if (classesList[classIndex] === "dynamic-width") {
765 shouldRemoveDynamicWidthClass = classIndex;
766 break;
767 }
768 }
769
770 if (shouldRemoveDynamicWidthClass !== false) {
771 classesList.splice(shouldRemoveDynamicWidthClass, 1);
772
773 if (classesList.length === 0) {
774 delete attributes.class;
775 }
776
777 attributes.class = classesList.join(" ");
778 }
779
780 shouldRemoveDynamicWidthClass = classesList = classIndex = null;
781 }
782
783 if (isEncoded && applyShortcode) {
784 var shortcode = '[' + $data.EMBEDPRESS_SHORTCODE;
785 if (!!Object.keys(attributes).length) {
786 var attrValue;
787
788 for (var attrName in attributes) {
789 attrValue = attributes[attrName];
790
791 // Prevent `class` property being empty an treated as a boolean param
792 if (attrName.toLowerCase() === "class" && !attrValue.length) {
793 continue;
794 }
795 else {
796 if (attrValue.isBoolean()) {
797 shortcode += " ";
798 if (attrValue.isFalse()) {
799 shortcode += "!";
800 }
801
802 shortcode += attrName
803 } else {
804 shortcode += ' '+ attrName +'="'+ attrValue +'"';
805 }
806 }
807 }
808 attrValue = attrName = null;
809 }
810
811 content = shortcode + ']' + content + '[/' + $data.EMBEDPRESS_SHORTCODE + ']';
812 }
813
814 return content;
815 };
816
817 /**
818 * Method executed after find the editor. It will make additional
819 * configurations and add the content's stylesheets for the preview
820 *
821 * @return void
822 */
823 self.onFindEditor = function(editorInstance) {
824 self.each = tinymce.each;
825 self.extend = tinymce.extend;
826 self.JSON = tinymce.util.JSON;
827 self.Node = tinymce.html.Node;
828
829 function onFindEditorCallback() {
830 $(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) + '">'));
831
832 self.addStylesheet(PLG_SYSTEM_ASSETS_CSS_PATH + '/font.css?v=' + self.params.versionUID, editorInstance, editorInstance);
833 self.addStylesheet(PLG_SYSTEM_ASSETS_CSS_PATH + '/preview.css?v=' + self.params.versionUID, editorInstance, editorInstance);
834 self.addStylesheet(PLG_CONTENT_ASSETS_CSS_PATH + '/embedpress.css?v=' + self.params.versionUID, editorInstance, editorInstance);
835 self.addEvent('nodechange', editorInstance, self.onNodeChange);
836 self.addEvent('keydown', editorInstance, function(e) {
837 self.onKeyDown(e, editorInstance);
838 });
839
840 var onUndoCallback = function(e) {
841 self.onUndo(e, editorInstance);
842 };
843
844 self.addEvent('undo', editorInstance, onUndoCallback); // TinyMCE
845 self.addEvent('undo', editorInstance.undoManager, onUndoCallback); // JCE
846
847 var doc = editorInstance.getDoc();
848 $(doc).on('mouseenter', '.embedpress_wrapper', function(e) {
849 self.onMouseEnter(e, editorInstance);
850 });
851 $(doc).on('mouseout', '.embedpress_wrapper', self.onMouseOut);
852 $(doc).on('mousedown', '.embedpress_wrapper > .embedpress_controller_panel', function(e) {
853 self.cancelEvent(e, editorInstance)
854 });
855 doc = null;
856
857 // Add the node filter that will convert the url into the preview box for the embed code
858 editorInstance.parser.addNodeFilter('#text', function addNodeFilterIntoParser(nodes, arg) {
859 self.each(nodes, function eachNodeInParser(node) {
860 // Stop if the node is "isolated". It would generate an error in the browser console and break.
861 if (node.parent === null && node.prev === null) {
862 return;
863 }
864
865 var subject = node.value.trim();
866
867
868 if (!subject.isValidUrl()) {
869 if (!subject.match(SHORTCODE_REGEXP)) {
870 return;
871 }
872 }
873 subject = self.decodeEmbedURLSpecialChars(subject);
874 if (!subject.isValidUrl()) {
875 if (!subject.match(SHORTCODE_REGEXP)) {
876 return;
877 }
878 }
879
880
881 subject = node.value.stripShortcode($data.EMBEDPRESS_SHORTCODE).trim();
882
883 // These patterns need to have groups for the pre and post texts
884 // @TODO: maybe remove this list of URLs? Let the server side code decide what URL should be parsed
885 var patterns = self.getProvidersURLPatterns();
886
887 (function tryToMatchContentAgainstUrlPatternWithIndex(urlPatternIndex) {
888 if (urlPatternIndex < patterns.length) {
889 var urlPattern = patterns[urlPatternIndex];
890 var urlPatternRegex = new RegExp(urlPattern);
891
892 var url = self.decodeEmbedURLSpecialChars(subject).trim();
893
894 var matches = url.match(urlPatternRegex);
895 // Check if content matches the url pattern.
896 if (matches && matches !== null && !!matches.length) {
897 url = self.encodeEmbedURLSpecialChars(matches[2]);
898
899 var wrapper = self.addURLsPlaceholder(node, url, editorInstance);
900
901 setTimeout(function() {
902 var doc = editorInstance.getDoc();
903
904 if (doc === null) {
905 return;
906 }
907
908 var previewWrapper = $(doc.querySelector('#'+ wrapper.attributes.map['id']));
909 var previewWrapperParent = $(previewWrapper.parent());
910
911 if (previewWrapperParent && previewWrapperParent.prop('tagName') && previewWrapperParent.prop('tagName').toUpperCase() === "P") {
912 previewWrapperParent.replaceWith(previewWrapper);
913 }
914
915 var previewWrapperOlderSibling = previewWrapper.prev();
916 if (previewWrapperOlderSibling && previewWrapperOlderSibling.prop('tagName') && previewWrapperOlderSibling.prop('tagName').toUpperCase() === "P" && !previewWrapperOlderSibling.html().replace(/\&nbsp\;/i, '').length) {
917 previewWrapperOlderSibling.remove();
918 } else {
919 if (typeof previewWrapperOlderSibling.html() !== 'undefined') {
920 if (previewWrapperOlderSibling.html().match(/<[\/]?br>/)) {
921 if (!previewWrapperOlderSibling.prev().length) {
922 previewWrapperOlderSibling.remove();
923 }
924 }
925 }
926 }
927
928 var previewWrapperYoungerSibling = previewWrapper.next();
929 if (previewWrapperYoungerSibling && previewWrapperYoungerSibling.length && previewWrapperYoungerSibling.prop('tagName').toUpperCase() === "P") {
930 if (!previewWrapperYoungerSibling.next().length && !previewWrapperYoungerSibling.html().replace(/\&nbsp\;/i, '').length) {
931 previewWrapperYoungerSibling.remove();
932 $('<p>&nbsp;</p>').insertAfter(previewWrapper);
933 }
934 } else {
935 $('<p>&nbsp;</p>').insertAfter(previewWrapper);
936 }
937
938 setTimeout(function() {
939 editorInstance.selection.select(editorInstance.getBody(), true);
940 editorInstance.selection.collapse(false);
941 }, 50);
942 }, 50);
943 } else {
944 // No match. So we move on to check the next url pattern.
945 tryToMatchContentAgainstUrlPatternWithIndex(urlPatternIndex + 1);
946 }
947 }
948 })(0);
949 });
950 });
951
952 // Add the filter that will convert the preview box/embed code back to the raw url
953 editorInstance.serializer.addNodeFilter('div', function addNodeFilterIntoSerializer(nodes, arg) {
954 self.each(nodes, function eachNodeInSerializer(node) {
955 // Stop if the node is "isolated". It would generate an error in the browser console and break.
956 if (node.parent === null && node.prev === null) {
957 return;
958 }
959
960 var nodeClasses = (node.attributes.map.class || "").split(' ');
961 var wrapperFactoryClasses = ["embedpress_wrapper", "embedpress_placeholder", "wpview", "wpview-wrap"];
962
963 var isWrapped = nodeClasses.filter(function(n) {
964 return wrapperFactoryClasses.indexOf(n) != -1;
965 }).length > 0;
966
967 if (isWrapped) {
968 var factoryAttributes = ["id", "style", "data-loading-text", "data-uid", "data-url"];
969 var customAttributes = {};
970 var dataPrefix = "data-";
971 for (var attr in node.attributes.map) {
972 if (attr.toLowerCase() !== "class") {
973 if (factoryAttributes.indexOf(attr) < 0) {
974 // Remove the "data-" prefix for more readability
975 customAttributes[attr.replace(dataPrefix, "")] = node.attributes.map[attr];
976 }
977 } else {
978 var customClasses = [];
979 for (var wrapperClassIndex in nodeClasses) {
980 var wrapperClass = nodeClasses[wrapperClassIndex];
981 if (wrapperFactoryClasses.indexOf(wrapperClass) === -1) {
982 customClasses.push(wrapperClass);
983 }
984 }
985
986 if (!!customClasses.length) {
987 customAttributes.class = customClasses.join(" ");
988 }
989 }
990 }
991
992 var p = new self.Node('p', 1);
993
994 var text = new self.Node('#text', 3);
995 text.value = self.decodeEmbedURLSpecialChars(node.attributes.map['data-url'].trim(), true, customAttributes);
996
997 p.append(text.clone());
998
999 node.replace(text);
1000 text.replace(p);
1001 }
1002 });
1003 });
1004
1005 editorInstance.serializer.addNodeFilter('p', function addNodeFilterIntoSerializer(nodes, arg) {
1006 self.each(nodes, function eachNodeInSerializer(node) {
1007 if (node.firstChild == node.lastChild) {
1008 if (node.firstChild && "value" in node.firstChild && (node.firstChild.value === "&nbsp;" || !node.firstChild.value.trim().length)) {
1009 node.remove();
1010 }
1011 }
1012 });
1013 });
1014
1015 //@todo:isthiseachreallynecessary?
1016 // Add event to reconfigure wrappers every time the content is loaded
1017 tinymce.each(tinymce.editors, function onEachEditor(editor) {
1018 self.addEvent('loadContent', editor, function onInitEditor(ed) {
1019 self.configureWrappers(editor);
1020 });
1021 });
1022
1023 // Add the edit form
1024
1025 // @todo: This is needed only for JCE, to fix the img placeholder. Try to find out a better approach to avoid the placeholder blink
1026 window.setTimeout(
1027 function afterTimeoutOnFindEditor() {
1028 /*
1029 * This is required because after load/refresh the page, the
1030 * onLoadContent is not being triggered automatically, so
1031 * we force the event
1032 */
1033 editorInstance.load();
1034 },
1035 // If in JCE the user see the placeholder (img) instead of the iframe after load/refresh the pagr, this time is too short
1036 500
1037 );
1038 }
1039
1040 // Let's make sure the inner doc has been fully loaded first.
1041 var checkTimesLimit = 100;
1042 var checkIndex = 0;
1043 var statusCheckerInterval = setInterval(function() {
1044 if (checkIndex === checkTimesLimit) {
1045 clearInterval(statusCheckerInterval);
1046 alert('For some reason TinyMCE was not fully loaded yet. Please, refresh the page and try again.');
1047 } else {
1048 var doc = editorInstance.getDoc();
1049 if (doc) {
1050 clearInterval(statusCheckerInterval);
1051 onFindEditorCallback();
1052 } else {
1053 checkIndex++;
1054 }
1055 }
1056 }, 250);
1057 };
1058
1059 self.fixIframeSize = function(iframe) {
1060 var maxWidth = 480;
1061 if ($(iframe).width() > maxWidth && !$(iframe).data('size-fixed')) {
1062 var ratio = $(iframe).height() / $(iframe).width();
1063 $(iframe).width(maxWidth);
1064 $(iframe).height(maxWidth * ratio);
1065 $(iframe).css('max-width', maxWidth);
1066 $(iframe).attr('max-width', maxWidth);
1067
1068 $(iframe).data('size-fixed', true);
1069 }
1070 }
1071
1072 /**
1073 * Function triggered on mouse enter the wrapper
1074 *
1075 * @param object e The event
1076 * @return void
1077 */
1078 self.onMouseEnter = function(e, editorInstance) {
1079 self.displayPreviewControllerPanel($(e.currentTarget), editorInstance);
1080 };
1081
1082 /**
1083 * Function triggered on mouse get out of the wrapper
1084 *
1085 * @param object e The event
1086 * @return void
1087 */
1088 self.onMouseOut = function(e) {
1089 // Check if the destiny is not a child element
1090 // Chrome
1091 if (self.isDefined(e.toElement)) {
1092 if (e.toElement.parentElement == e.fromElement
1093 || $(e.toElement).hasClass('embedpress_ignore_mouseout')
1094 ) {
1095 return false;
1096 }
1097 }
1098
1099 // Firefox
1100 if (self.isDefined(e.relatedTarget)) {
1101 if ($(e.relatedTarget).hasClass('embedpress_ignore_mouseout')) {
1102 return false;
1103 }
1104 }
1105
1106 self.hidePreviewControllerPanel();
1107 };
1108
1109 /**
1110 * Callback triggered by paste events. This should be hooked by TinyMCE's paste_preprocess
1111 * setting. A normal bind to the onPaste event doesn't work correctly all the times
1112 * (specially when you copy and paste content from the same editor).
1113 *
1114 * @param mixed - plugin
1115 * @param mixed - args
1116 *
1117 * @return void
1118 */
1119
1120 self.onPaste = function(plugin, args) {
1121 var urlPatternRegex = new RegExp(/(https?):\/\/([w]{3}\.)?.+?(?:\s|$)/i);
1122 var urlPatternsList = self.getProvidersURLPatterns();
1123
1124 // Split the pasted content into separated lines.
1125 var contentLines = args.content.split(/\n/g) || [];
1126 contentLines = contentLines.map(function(line, itemIndex) {
1127 // Check if there's a url into `line`.
1128 if (line.match(urlPatternRegex)) {
1129 // Split the current line across its space-characters to isolate the url.
1130 let termsList = line.trim().split(/\s+/);
1131 termsList = termsList.map(function(term, termIndex) {
1132 // Check if the term into the current line is a url.
1133 var match = term.match(urlPatternRegex);
1134 if (match) {
1135 for (var urlPatternIndex = 0; urlPatternIndex < urlPatternsList.length; urlPatternIndex++) {
1136 // Isolates that url from the rest of the content if the service is supported.
1137 var urlPattern = new RegExp(urlPatternsList[urlPatternIndex]);
1138 if (urlPattern.test(term)) {
1139 return '</p><p>'+ match[0] +'</p><p>';
1140 }
1141 }
1142 }
1143
1144 return term;
1145 });
1146
1147 termsList[termsList.length - 1] = termsList[termsList.length - 1] + '<br>';
1148
1149 line = termsList.join(' ');
1150 }
1151
1152 return line;
1153 });
1154
1155 // Check if the text was transformed or not. If it was, add wrappers
1156 var content = contentLines.join('');
1157
1158 if (content.replace(/<br>$/, '') !== args.content) {
1159 args.content = '<p>'+ args.content +'</p>';
1160 }
1161 };
1162
1163 /**
1164 * Method trigered on every node change, to detect new lines. It will
1165 * try to fix a default behavior for some editors of clone the parent
1166 * element when adding a line break. This will clone the embed wrapper
1167 * if we set the cursor after a preview wrapper and hit enter.
1168 *
1169 * @param object e The event
1170 * @return void
1171 */
1172 self.onNodeChange = function(e) {
1173 // Fix the clone parent on break lines issue
1174 // Check if a line break was added
1175 if (e.element.tagName === 'BR') {
1176 // Check one of the parent elements is a clonned embed wrapper
1177 if (e.parents.length > 0) {
1178 $.each(e.parents, function(index, parent) {
1179 if ($(parent).hasClass('embedpress_wrapper')) {
1180 // Remove the cloned wrapper and replace with a 'br' tag
1181 $(parent).replaceWith($('<br>'));
1182 }
1183 });
1184 }
1185 } else if (e.element.tagName === "IFRAME") {
1186 if (e.parents.length > 0) {
1187 $.each(e.parents, function(index, parent) {
1188 parent = $(parent);
1189 if (parent.hasClass('embedpress_wrapper')) {
1190 var wrapper = $('.embedpress-wrapper', parent);
1191 if (wrapper.length > 1) {
1192 wrapper.get(0).remove();
1193 }
1194 }
1195 });
1196 }
1197 }
1198 };
1199
1200 self.onKeyDown = function(e, editorInstance) {
1201 var node = editorInstance.selection.getNode();
1202
1203 if (e.keyCode == 8 || e.keyCode == 46) {
1204 if (node.nodeName.toLowerCase() === 'p') {
1205 var children = $(node).children();
1206 if (children.length > 0) {
1207 $.each(children, function() {
1208 // On delete, make sure to remove the wrapper and children, not only the wrapper
1209 if ($(this).hasClass('embedpress_wrapper') || $(this).hasClass('embedpress_ignore_mouseout')) {
1210 $(this).remove();
1211
1212 editorInstance.focus();
1213 }
1214 });
1215 }
1216 }
1217 } else {
1218 // Ignore the arrows keys
1219 var arrowsKeyCodes = [37, 38, 39, 40];
1220 if (arrowsKeyCodes.indexOf(e.keyCode) == -1) {
1221
1222 // Check if we are inside a preview wrapper
1223 if ($(node).hasClass('embedpress_wrapper') || $(node).hasClass('embedpress_ignore_mouseout')) {
1224 // Avoid delete the wrapper or block line break if we are inside the wrapper
1225 if (e.keyCode == 13) {
1226 wrapper = $(self.getWrapperFromChild(node));
1227 if (wrapper.length > 0) {
1228 // Creates a temporary element which will be inserted after the wrapper
1229 var tmpId = '__embedpress__tmp_' + self.makeId();
1230 wrapper.after($('<span id="' + tmpId + '"></span>'));
1231 // Get and select the temporary element
1232 var span = editorInstance.dom.select('span#' + tmpId)[0];
1233 editorInstance.selection.select(span);
1234 // Remove the temporary element
1235 $(span).remove();
1236 }
1237
1238 return true;
1239 } else {
1240 // If we are inside the embed preview, ignore any key to avoid edition
1241 return self.cancelEvent(e, editorInstance);
1242 }
1243 }
1244 }
1245 }
1246
1247 return true;
1248 }
1249
1250 self.getWrapperFromChild = function(element) {
1251 // Is the wrapper
1252 if ($(element).hasClass('embedpress_wrapper')) {
1253 return element;
1254 } else {
1255 var $parent = $(element).parent();
1256
1257 if ($parent.length > 0) {
1258 return self.getWrapperFromChild($parent[0]);
1259 }
1260 }
1261
1262 return false;
1263 };
1264
1265 self.onUndo = function(e, editorInstance) {
1266 // Force re-render everything
1267 editorInstance.load();
1268 };
1269
1270 self.cancelEvent = function(e, editorInstance) {
1271 e.preventDefault();
1272 e.stopPropagation();
1273 editorInstance.dom.events.cancel();
1274
1275 return false;
1276 };
1277
1278 /**
1279 * Method executed when the edit button is clicked. It will display
1280 * a field with the current url, to update the current embed's source
1281 * url.
1282 *
1283 * @param Object e The event
1284 * @return void
1285 */
1286 self.onClickEditButton = function(e, editorInstance) {
1287 // Prevent edition of the panel
1288 self.cancelEvent(e, editorInstance);
1289
1290 self.activeWrapperForModal = self.activeWrapper;
1291
1292 var $wrapper = self.activeWrapperForModal;
1293 var wrapperUid = $wrapper.prop('id').replace("embedpress_wrapper_", "");
1294
1295 var customAttributes = {};
1296
1297 var $embedInnerWrapper = $('.embedpress-wrapper', $wrapper);
1298 var embedItem = $('iframe', $wrapper);
1299 if (!embedItem.length) {
1300 embedItem = null;
1301 }
1302
1303 $.each($embedInnerWrapper[0].attributes, function() {
1304 if (this.specified) {
1305 if (this.name !== "class") {
1306 customAttributes[this.name.replace('data-', "").toLowerCase()] = this.value;
1307 }
1308 }
1309 });
1310
1311 var embedWidth = (((embedItem && embedItem.width()) || $embedInnerWrapper.data('width')) || $embedInnerWrapper.width()) || "";
1312 var embedHeight = (((embedItem && embedItem.height()) || $embedInnerWrapper.data('height')) || $embedInnerWrapper.height()) || "";
1313
1314 embedItem = $embedInnerWrapper = null;
1315
1316 $('<div class="loader-indicator"><i class="embedpress-icon-reload"></i></div>').appendTo($wrapper);
1317
1318 setTimeout(function() {
1319 $.ajax({
1320 type: "GET",
1321 url: self.params.baseUrl +"wp-admin/admin-ajax.php",
1322 data: {
1323 action: "embedpress_get_embed_url_info",
1324 url: self.decodeEmbedURLSpecialChars($wrapper.data('url'), false)
1325 },
1326 beforeSend: function(request, requestSettings) {
1327 $('.loader-indicator', $wrapper).addClass('is-loading');
1328 },
1329 success: function(response) {
1330 if (!response) {
1331 bootbox.alert('Unable to get a valid response from the server.');
1332 return;
1333 }
1334 if (response.canBeResponsive) {
1335 var embedShouldBeResponsive = true;
1336 if ("width" in customAttributes || "height" in customAttributes) {
1337 embedShouldBeResponsive = false;
1338 } else if ("responsive" in customAttributes && customAttributes['responsive'].isFalse()) {
1339 embedShouldBeResponsive = false;
1340 }
1341 }
1342
1343 bootbox.dialog({
1344 className: "embedpress-modal",
1345 title: "Editing Embed properties",
1346 message: '<form id="form-'+ wrapperUid +'" embedpress>'+
1347 '<div class="row">'+
1348 '<div class="col-md-12">'+
1349 '<div class="form-group">'+
1350 '<label for="input-url-'+ wrapperUid +'">Url</label>'+
1351 '<input class="form-control" type="url" id="input-url-'+ wrapperUid +'" value="'+ self.decodeEmbedURLSpecialChars($wrapper.data('url'), false) +'">'+
1352 '</div>'+
1353 '</div>'+
1354 '</div>'+
1355 '<div class="row">'+
1356 (response.canBeResponsive ?
1357 '<div class="col-md-12">'+
1358 '<label>Responsive</label>'+
1359 '<div class="form-group">'+
1360 '<label class="radio-inline">'+
1361 '<input type="radio" name="input-responsive-'+ wrapperUid +'" id="input-responsive-1-'+ wrapperUid +'" value="1"'+ (embedShouldBeResponsive ? ' checked="checked"' : '') +'> Yes'+
1362 '</label>'+
1363 '<label class="radio-inline">'+
1364 '<input type="radio" name="input-responsive-'+ wrapperUid +'" id="input-responsive-0-'+ wrapperUid +'" value="0"'+ (!embedShouldBeResponsive ? ' checked="checked"' : '') +'> No'+
1365 '</label>'+
1366 '</div>'+
1367 '</div>' : '')+
1368 '<div class="col-md-6">'+
1369 '<div class="form-group">'+
1370 '<label for="input-width-'+ wrapperUid +'">Width</label>'+
1371 '<input class="form-control" type="integer" id="input-width-'+ wrapperUid +'" value="'+ embedWidth +'"'+ (embedShouldBeResponsive ? ' disabled' : '') +'>'+
1372 '</div>'+
1373 '</div>'+
1374 '<div class="col-md-6">'+
1375 '<div class="form-group">'+
1376 '<label for="input-height-'+ wrapperUid +'">Height</label>'+
1377 '<input class="form-control" type="integer" id="input-height-'+ wrapperUid +'" value="'+ embedHeight +'"'+ (embedShouldBeResponsive ? ' disabled' : '') +'>'+
1378 '</div>'+
1379 '</div>'+
1380 '</div>'+
1381 '</form>',
1382 buttons: {
1383 danger: {
1384 label: "Cancel",
1385 className: "btn-default",
1386 callback: function() {
1387 // do nothing
1388 self.activeWrapperForModal = null;
1389 }
1390 },
1391 success: {
1392 label: "Save",
1393 className: "btn-primary",
1394 callback: function() {
1395 var $wrapper = self.activeWrapperForModal;
1396
1397 // Select the current wrapper as a base for the new element
1398 editorInstance.focus();
1399 editorInstance.selection.select($wrapper[0]);
1400
1401 $wrapper.children().remove();
1402 $wrapper.remove();
1403
1404 if (response.canBeResponsive) {
1405 if ($('#form-'+ wrapperUid +' input[name="input-responsive-'+ wrapperUid +'"]:checked').val().isFalse()) {
1406 var embedCustomWidth = $('#input-width-'+ wrapperUid).val();
1407 if (parseInt(embedCustomWidth) > 0) {
1408 customAttributes['width'] = embedCustomWidth;
1409 }
1410
1411 var embedCustomHeight = $('#input-height-'+ wrapperUid).val();
1412 if (parseInt(embedCustomHeight) > 0) {
1413 customAttributes['height'] = embedCustomHeight;
1414 }
1415
1416 customAttributes['responsive'] = "false";
1417 } else {
1418 delete customAttributes['width'];
1419 delete customAttributes['height'];
1420
1421 customAttributes['responsive'] = "true";
1422 }
1423 } else {
1424 var embedCustomWidth = $('#input-width-'+ wrapperUid).val();
1425 if (parseInt(embedCustomWidth) > 0) {
1426 customAttributes['width'] = embedCustomWidth;
1427 }
1428
1429 var embedCustomHeight = $('#input-height-'+ wrapperUid).val();
1430 if (parseInt(embedCustomHeight) > 0) {
1431 customAttributes['height'] = embedCustomHeight;
1432 }
1433 }
1434
1435 var customAttributesList = [];
1436 if (!!Object.keys(customAttributes).length) {
1437 for (var attrName in customAttributes) {
1438 customAttributesList.push(attrName + '="' + customAttributes[attrName] + '"');
1439 }
1440 }
1441
1442 var shortcode = '['+ $data.EMBEDPRESS_SHORTCODE + (customAttributesList.length > 0 ? " "+ customAttributesList.join(" ") : "") +']'+ $('#input-url-'+ wrapperUid).val() +'[/'+ $data.EMBEDPRESS_SHORTCODE +']';
1443 // We do not directly replace the node because it was causing a bug on a second edit attempt
1444 editorInstance.execCommand('mceInsertContent', false, shortcode);
1445
1446 self.configureWrappers(editorInstance);
1447 }
1448 }
1449 }
1450 });
1451
1452 $('form[embedpress]').on('change', 'input[type="radio"]', function(e) {
1453 var self = $(this);
1454 var form = self.parents('form[embedpress]');
1455
1456 $('input[type="integer"]', form).prop('disabled', self.val().isTrue());
1457 });
1458 },
1459 complete: function(request, textStatus) {
1460 $('.loader-indicator', $wrapper).removeClass('is-loading');
1461
1462 setTimeout(function() {
1463 $('.loader-indicator', $wrapper).remove();
1464 }, 350);
1465 },
1466 dataType: "json",
1467 async: true
1468 });
1469 }, 200);
1470
1471 return false;
1472 };
1473
1474 /**
1475 * Method executed when the remove button is clicked. It will remove
1476 * the preview and embed code, adding a mark to ignore the url
1477 *
1478 * @param Object e The event
1479 * @return void
1480 */
1481 self.onClickRemoveButton = function(e, editorInstance) {
1482 // Prevent edition of the panel
1483 self.cancelEvent(e, editorInstance);
1484
1485 var $wrapper = self.activeWrapper;
1486
1487 $wrapper.children().remove();
1488 $wrapper.remove();
1489
1490 return false;
1491 };
1492
1493 self.recursivelyAddClass = function(element, className) {
1494 $(element).children().each(function(index, child) {
1495 $(child).addClass(className);
1496
1497 var grandChild = $(child).children();
1498 if (grandChild.length > 0) {
1499 self.recursivelyAddClass(child, className)
1500 }
1501 });
1502 };
1503
1504 self.setInterval = function(callback, time, timeout) {
1505 var elapsed = 0;
1506 var iteraction = 0;
1507
1508 var interval = window.setInterval(function() {
1509 elapsed += time;
1510 iteraction++;
1511
1512 if (elapsed <= timeout) {
1513 callback(iteraction, elapsed);
1514 } else {
1515 self.stopInterval(interval);
1516 }
1517 }, time);
1518
1519 return interval;
1520 };
1521
1522 self.stopInterval = function(interval) {
1523 window.clearInterval(interval);
1524 interval = null;
1525 };
1526
1527 /**
1528 * Configure unconfigured embed wrappers, adding events and css
1529 * @return void
1530 */
1531 self.configureWrappers = function(editorInstance) {
1532 window.setTimeout(
1533 function configureWrappersTimeOut() {
1534 var doc = editorInstance.getDoc(),
1535 $wrapper = null;
1536
1537 // Get all the wrappers
1538 var wrappers = doc.getElementsByClassName('embedpress_wrapper');
1539 if (wrappers.length > 0) {
1540 for (var i = 0; i < wrappers.length; i++) {
1541 $wrapper = $(wrappers[i]);
1542
1543 // Check if the wrapper wasn't already configured
1544 if ($wrapper.data('configured') != true) {
1545 // A timeout was set to avoid block the content loading
1546 window.setTimeout(function() {
1547 // @todo: Check if we need a limit of levels to avoid use too much resources
1548 self.recursivelyAddClass($wrapper, 'embedpress_ignore_mouseout');
1549 }, 500);
1550
1551 // Fix the wrapper size. Wait until find the child iframe. L
1552 var interval = self.setInterval(function(iteraction) {
1553 var $childIframes = $wrapper.find('iframe');
1554
1555 if ($childIframes.length > 0) {
1556 $.each($childIframes, function(index, iframe) {
1557 // Facebook has more than one iframe, we need to ignore the Cross Domain Iframes
1558 if ($(iframe).attr('id') !== 'fb_xdm_frame_https'
1559 && $(iframe).attr('id') !== 'fb_xdm_frame_http'
1560 ) {
1561 $wrapper.css('width', $(iframe).width() + 'px');
1562 self.stopInterval(interval);
1563 }
1564 });
1565 }
1566 }, 500, 8000);
1567
1568 $wrapper.data('configured', true);
1569 }
1570 }
1571 }
1572 },
1573 200
1574 );
1575 };
1576
1577 /**
1578 * Hide the controller panel
1579 *
1580 * @return void
1581 */
1582 self.hidePreviewControllerPanel = function() {
1583 if (self.controllerPanelIsActive()) {
1584 $(self.activeControllerPanel).addClass('hidden');
1585 self.activeControllerPanel = null;
1586 self.activeWrapper = null;
1587 }
1588 };
1589
1590 /**
1591 * Get an element by id in the editor's content
1592 *
1593 * @param String id The element id
1594 * @return Element The found element or null, wrapped by jQuery
1595 */
1596 self.getElementInContentById = function(id, editorInstance) {
1597 var doc = editorInstance.getDoc();
1598
1599 if (doc === null) {
1600 return;
1601 }
1602
1603 return $(doc.getElementById(id));
1604 };
1605
1606 /**
1607 * Show the controller panel
1608 *
1609 * @param element $wrapper The wrapper which will be activate
1610 * @return void
1611 */
1612 self.displayPreviewControllerPanel = function($wrapper, editorInstance) {
1613 if (self.controllerPanelIsActive() && $wrapper !== self.activeWrapper) {
1614 self.hidePreviewControllerPanel();
1615 }
1616
1617 if (!self.controllerPanelIsActive() && !$wrapper.hasClass('is-loading')) {
1618 var uid = $wrapper.data('uid');
1619 var $panel = self.getElementInContentById('embedpress_controller_panel_' + uid, editorInstance);
1620
1621 if (!$panel.data('event-set')) {
1622 var $editButton = self.getElementInContentById('embedpress_button_edit_' + uid, editorInstance);
1623 var $removeButton = self.getElementInContentById('embedpress_button_remove_' + uid, editorInstance);
1624
1625 self.addEvent('mousedown', $editButton, function(e) {
1626 self.onClickEditButton(e, editorInstance);
1627 });
1628
1629 self.addEvent('mousedown', $removeButton, function(e) {
1630 self.onClickRemoveButton(e, editorInstance);
1631 });
1632
1633 $panel.data('event-set', true);
1634 }
1635
1636 // Update the position of the control bar
1637 var next = $panel.next()[0];
1638 if (typeof next !== 'undefined') {
1639 if (next.nodeName.toLowerCase() === 'iframe') {
1640 $panel.css('left', ($(next).width() / 2));
1641 }
1642 }
1643
1644 // Show the bar
1645 $panel.removeClass('hidden');
1646
1647 self.activeControllerPanel = $panel;
1648 self.activeWrapper = $wrapper;
1649 }
1650 };
1651 };
1652
1653 if (!window.EmbedPress) {
1654 window.EmbedPress = new EmbedPress();
1655 }
1656
1657 // Initialize EmbedPress for all the current editors.
1658 window.EmbedPress.init($data.previewSettings);
1659 });
1660 })(jQuery, String, $data);
1661