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