PluginProbe ʕ •ᴥ•ʔ
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more / 1.7.3
EmbedPress – PDF Embedder, Embed PDF viewer, YouTube Videos, 3D FlipBook, Social feeds & more v1.7.3
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
1624 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?|ftp|embedpresss?):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/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 // Add the node filter that will convert the url into the preview box for the embed code
837 // @todo: Recognize <a> tags as well
838
839 editorInstance.parser.addNodeFilter('#text', function addNodeFilterIntoParser(nodes, arg) {
840 self.each(nodes, function eachNodeInParser(node) {
841 var subject = node.value.trim();
842 if (!subject.isValidUrl()) {
843 if (!subject.match(SHORTCODE_REGEXP)) {
844 return;
845 }
846 }
847
848 subject = self.decodeEmbedURLSpecialChars(subject);
849 if (!subject.isValidUrl()) {
850 if (!subject.match(SHORTCODE_REGEXP)) {
851 return;
852 }
853 }
854
855 subject = node.value.stripShortcode($data.EMBEDPRESS_SHORTCODE).trim();
856
857 // These patterns need to have groups for the pre and post texts
858 var patterns = self.getProvidersURLPatterns();
859
860 (function tryToMatchContentAgainstUrlPatternWithIndex(urlPatternIndex) {
861 if (urlPatternIndex < patterns.length) {
862 var urlPattern = patterns[urlPatternIndex];
863 var urlPatternRegex = new RegExp(urlPattern);
864
865 var url = self.decodeEmbedURLSpecialChars(subject).trim();
866
867 var matches = url.match(urlPatternRegex);
868 // Check if content matches the url pattern.
869 if (matches && matches !== null && !!matches.length) {
870 url = self.encodeEmbedURLSpecialChars(matches[2]);
871
872 var wrapper = self.addURLsPlaceholder(node, url, editorInstance);
873
874 setTimeout(function() {
875 var doc = editorInstance.getDoc();
876
877 var previewWrapper = $(doc.querySelector('#'+ wrapper.attributes.map['id']));
878 var previewWrapperParent = $(previewWrapper.parent());
879
880 if (previewWrapperParent && previewWrapperParent.prop('tagName') && previewWrapperParent.prop('tagName').toUpperCase() === "P") {
881 previewWrapperParent.replaceWith(previewWrapper);
882 }
883
884 var previewWrapperOlderSibling = previewWrapper.prev();
885 if (previewWrapperOlderSibling && previewWrapperOlderSibling.prop('tagName') && previewWrapperOlderSibling.prop('tagName').toUpperCase() === "P" && !previewWrapperOlderSibling.html().replace(/\&nbsp\;/i, '').length) {
886 previewWrapperOlderSibling.remove();
887 } else {
888 if (typeof previewWrapperOlderSibling.html() !== 'undefined') {
889 if (previewWrapperOlderSibling.html().match(/<[\/]?br>/)) {
890 if (!previewWrapperOlderSibling.prev().length) {
891 previewWrapperOlderSibling.remove();
892 }
893 }
894 }
895 }
896
897 var previewWrapperYoungerSibling = previewWrapper.next();
898 if (previewWrapperYoungerSibling && previewWrapperYoungerSibling.length && previewWrapperYoungerSibling.prop('tagName').toUpperCase() === "P") {
899 if (!previewWrapperYoungerSibling.next().length && !previewWrapperYoungerSibling.html().replace(/\&nbsp\;/i, '').length) {
900 previewWrapperYoungerSibling.remove();
901 $('<p>&nbsp;</p>').insertAfter(previewWrapper);
902 }
903 } else {
904 $('<p>&nbsp;</p>').insertAfter(previewWrapper);
905 }
906
907 setTimeout(function() {
908 editorInstance.selection.select(editorInstance.getBody(), true);
909 editorInstance.selection.collapse(false);
910 }, 50);
911 }, 50);
912 } else {
913 // No match. So we move on to check the next url pattern.
914 tryToMatchContentAgainstUrlPatternWithIndex(urlPatternIndex + 1);
915 }
916 }
917 })(0);
918 });
919 });
920
921 // Add the filter that will convert the preview box/embed code back to the raw url
922 editorInstance.serializer.addNodeFilter('div', function addNodeFilterIntoSerializer(nodes, arg) {
923 self.each(nodes, function eachNodeInSerializer(node) {
924 var nodeClasses = (node.attributes.map.class || "").split(' ');
925 var wrapperFactoryClasses = ["embedpress_wrapper", "embedpress_placeholder", "wpview", "wpview-wrap"];
926
927 var isWrapped = nodeClasses.filter(function(n) {
928 return wrapperFactoryClasses.indexOf(n) != -1;
929 }).length > 0;
930
931 if (isWrapped) {
932 var factoryAttributes = ["id", "style", "data-loading-text", "data-uid", "data-url"];
933 var customAttributes = {};
934 var dataPrefix = "data-";
935 for (var attr in node.attributes.map) {
936 if (attr.toLowerCase() !== "class") {
937 if (factoryAttributes.indexOf(attr) < 0) {
938 // Remove the "data-" prefix for more readability
939 customAttributes[attr.replace(dataPrefix, "")] = node.attributes.map[attr];
940 }
941 } else {
942 var customClasses = [];
943 for (var wrapperClassIndex in nodeClasses) {
944 var wrapperClass = nodeClasses[wrapperClassIndex];
945 if (wrapperFactoryClasses.indexOf(wrapperClass) === -1) {
946 customClasses.push(wrapperClass);
947 }
948 }
949
950 if (!!customClasses.length) {
951 customAttributes.class = customClasses.join(" ");
952 }
953 }
954 }
955
956 var p = new self.Node('p', 1);
957
958 var text = new self.Node('#text', 3);
959 text.value = self.decodeEmbedURLSpecialChars(node.attributes.map['data-url'].trim(), true, customAttributes);
960
961 p.append(text.clone());
962
963 node.replace(text);
964 text.replace(p);
965 }
966 });
967 });
968
969 editorInstance.serializer.addNodeFilter('p', function addNodeFilterIntoSerializer(nodes, arg) {
970 self.each(nodes, function eachNodeInSerializer(node) {
971 if (node.firstChild == node.lastChild) {
972 if (node.firstChild && "value" in node.firstChild && (node.firstChild.value === "&nbsp;" || !node.firstChild.value.trim().length)) {
973 node.remove();
974 }
975 }
976 });
977 });
978
979 //@todo:isthiseachreallynecessary?
980 // Add event to reconfigure wrappers every time the content is loaded
981 tinymce.each(tinymce.editors, function onEachEditor(editor) {
982 self.addEvent('loadContent', editor, function onInitEditor(ed) {
983 self.configureWrappers(editor);
984 });
985 });
986
987 // Add the edit form
988
989 // @todo: This is needed only for JCE, to fix the img placeholder. Try to find out a better approach to avoid the placeholder blink
990 window.setTimeout(
991 function afterTimeoutOnFindEditor() {
992 /*
993 * This is required because after load/refresh the page, the
994 * onLoadContent is not being triggered automatically, so
995 * we force the event
996 */
997 editorInstance.load();
998 },
999 // If in JCE the user see the placeholder (img) instead of the iframe after load/refresh the pagr, this time is too short
1000 500
1001 );
1002 }
1003
1004 // Let's make sure the inner doc has been fully loaded first.
1005 var checkTimesLimit = 100;
1006 var checkIndex = 0;
1007 var statusCheckerInterval = setInterval(function() {
1008 if (checkIndex === checkTimesLimit) {
1009 clearInterval(statusCheckerInterval);
1010 alert('For some reason TinyMCE was not fully loaded yet. Please, refresh the page and try again.');
1011 } else {
1012 var doc = editorInstance.getDoc();
1013 if (doc) {
1014 clearInterval(statusCheckerInterval);
1015 onFindEditorCallback();
1016 } else {
1017 checkIndex++;
1018 }
1019 }
1020 }, 250);
1021 };
1022
1023 self.fixIframeSize = function(iframe) {
1024 var maxWidth = 480;
1025 if ($(iframe).width() > maxWidth && !$(iframe).data('size-fixed')) {
1026 var ratio = $(iframe).height() / $(iframe).width();
1027 $(iframe).width(maxWidth);
1028 $(iframe).height(maxWidth * ratio);
1029 $(iframe).css('max-width', maxWidth);
1030 $(iframe).attr('max-width', maxWidth);
1031
1032 $(iframe).data('size-fixed', true);
1033 }
1034 }
1035
1036 /**
1037 * Function triggered on mouse enter the wrapper
1038 *
1039 * @param object e The event
1040 * @return void
1041 */
1042 self.onMouseEnter = function(e, editorInstance) {
1043 self.displayPreviewControllerPanel($(e.currentTarget), editorInstance);
1044 };
1045
1046 /**
1047 * Function triggered on mouse get out of the wrapper
1048 *
1049 * @param object e The event
1050 * @return void
1051 */
1052 self.onMouseOut = function(e) {
1053 // Check if the destiny is not a child element
1054 // Chrome
1055 if (self.isDefined(e.toElement)) {
1056 if (e.toElement.parentElement == e.fromElement
1057 || $(e.toElement).hasClass('embedpress_ignore_mouseout')
1058 ) {
1059 return false;
1060 }
1061 }
1062
1063 // Firefox
1064 if (self.isDefined(e.relatedTarget)) {
1065 if ($(e.relatedTarget).hasClass('embedpress_ignore_mouseout')) {
1066 return false;
1067 }
1068 }
1069
1070 self.hidePreviewControllerPanel();
1071 };
1072
1073 /**
1074 * Callback triggered by paste events. This should be hooked by TinyMCE's paste_preprocess
1075 * setting. A normal bind to the onPaste event doesn't work correctly all the times
1076 * (specially when you copy and paste content from the same editor).
1077 *
1078 * @param mixed - plugin
1079 * @param mixed - args
1080 *
1081 * @return void
1082 */
1083
1084 self.onPaste = function(plugin, args) {
1085 var urlPatternRegex = new RegExp(/(https?):\/\/([w]{3}\.)?.+?(?:\s|$)/i);
1086 var urlPatternsList = self.getProvidersURLPatterns();
1087
1088 // Split the pasted content into separated lines.
1089 var contentLines = args.content.split(/\n/g) || [];
1090 contentLines = contentLines.map(function(line, itemIndex) {
1091 // Check if there's a url into `line`.
1092 if (line.match(urlPatternRegex)) {
1093 // Split the current line across its space-characters to isolate the url.
1094 let termsList = line.trim().split(/\s+/);
1095 termsList = termsList.map(function(term, termIndex) {
1096 // Check if the term into the current line is a url.
1097 var match = term.match(urlPatternRegex);
1098 if (match) {
1099 for (var urlPatternIndex = 0; urlPatternIndex < urlPatternsList.length; urlPatternIndex++) {
1100 // Isolates that url from the rest of the content if the service is supported.
1101 var urlPattern = new RegExp(urlPatternsList[urlPatternIndex]);
1102 if (urlPattern.test(term)) {
1103 return '</p><p>'+ match[0] +'</p><p>';
1104 }
1105 }
1106 }
1107
1108 return term;
1109 });
1110
1111 termsList[termsList.length - 1] = termsList[termsList.length - 1] + '<br>';
1112
1113 line = termsList.join(' ');
1114 }
1115
1116 return line;
1117 });
1118
1119 // Check if the text was transformed or not. If it was, add wrappers
1120 var content = contentLines.join('');
1121
1122 if (content.replace(/<br>$/, '') !== args.content) {
1123 args.content = '<p>'+ args.content +'</p>';
1124 }
1125 };
1126
1127 /**
1128 * Method trigered on every node change, to detect new lines. It will
1129 * try to fix a default behavior for some editors of clone the parent
1130 * element when adding a line break. This will clone the embed wrapper
1131 * if we set the cursor after a preview wrapper and hit enter.
1132 *
1133 * @param object e The event
1134 * @return void
1135 */
1136 self.onNodeChange = function(e) {
1137 // Fix the clone parent on break lines issue
1138 // Check if a line break was added
1139 if (e.element.tagName === 'BR') {
1140 // Check one of the parent elements is a clonned embed wrapper
1141 if (e.parents.length > 0) {
1142 $.each(e.parents, function(index, parent) {
1143 if ($(parent).hasClass('embedpress_wrapper')) {
1144 // Remove the cloned wrapper and replace with a 'br' tag
1145 $(parent).replaceWith($('<br>'));
1146 }
1147 });
1148 }
1149 } else if (e.element.tagName === "IFRAME") {
1150 if (e.parents.length > 0) {
1151 $.each(e.parents, function(index, parent) {
1152 parent = $(parent);
1153 if (parent.hasClass('embedpress_wrapper')) {
1154 var wrapper = $('.embedpress-wrapper', parent);
1155 if (wrapper.length > 1) {
1156 wrapper.get(0).remove();
1157 }
1158 }
1159 });
1160 }
1161 }
1162 };
1163
1164 self.onKeyDown = function(e, editorInstance) {
1165 var node = editorInstance.selection.getNode();
1166
1167 if (e.keyCode == 8 || e.keyCode == 46) {
1168 if (node.nodeName.toLowerCase() === 'p') {
1169 var children = $(node).children();
1170 if (children.length > 0) {
1171 $.each(children, function() {
1172 // On delete, make sure to remove the wrapper and children, not only the wrapper
1173 if ($(this).hasClass('embedpress_wrapper') || $(this).hasClass('embedpress_ignore_mouseout')) {
1174 $(this).remove();
1175
1176 editorInstance.focus();
1177 }
1178 });
1179 }
1180 }
1181 } else {
1182 // Ignore the arrows keys
1183 var arrowsKeyCodes = [37, 38, 39, 40];
1184 if (arrowsKeyCodes.indexOf(e.keyCode) == -1) {
1185
1186 // Check if we are inside a preview wrapper
1187 if ($(node).hasClass('embedpress_wrapper') || $(node).hasClass('embedpress_ignore_mouseout')) {
1188 // Avoid delete the wrapper or block line break if we are inside the wrapper
1189 if (e.keyCode == 13) {
1190 wrapper = $(self.getWrapperFromChild(node));
1191 if (wrapper.length > 0) {
1192 // Creates a temporary element which will be inserted after the wrapper
1193 var tmpId = '__embedpress__tmp_' + self.makeId();
1194 wrapper.after($('<span id="' + tmpId + '"></span>'));
1195 // Get and select the temporary element
1196 var span = editorInstance.dom.select('span#' + tmpId)[0];
1197 editorInstance.selection.select(span);
1198 // Remove the temporary element
1199 $(span).remove();
1200 }
1201
1202 return true;
1203 } else {
1204 // If we are inside the embed preview, ignore any key to avoid edition
1205 return self.cancelEvent(e, editorInstance);
1206 }
1207 }
1208 }
1209 }
1210
1211 return true;
1212 }
1213
1214 self.getWrapperFromChild = function(element) {
1215 // Is the wrapper
1216 if ($(element).hasClass('embedpress_wrapper')) {
1217 return element;
1218 } else {
1219 var $parent = $(element).parent();
1220
1221 if ($parent.length > 0) {
1222 return self.getWrapperFromChild($parent[0]);
1223 }
1224 }
1225
1226 return false;
1227 };
1228
1229 self.onUndo = function(e, editorInstance) {
1230 // Force re-render everything
1231 editorInstance.load();
1232 };
1233
1234 self.cancelEvent = function(e, editorInstance) {
1235 e.preventDefault();
1236 e.stopPropagation();
1237 editorInstance.dom.events.cancel();
1238
1239 return false;
1240 };
1241
1242 /**
1243 * Method executed when the edit button is clicked. It will display
1244 * a field with the current url, to update the current embed's source
1245 * url.
1246 *
1247 * @param Object e The event
1248 * @return void
1249 */
1250 self.onClickEditButton = function(e, editorInstance) {
1251 // Prevent edition of the panel
1252 self.cancelEvent(e, editorInstance);
1253
1254 self.activeWrapperForModal = self.activeWrapper;
1255
1256 var $wrapper = self.activeWrapperForModal;
1257 var wrapperUid = $wrapper.prop('id').replace("embedpress_wrapper_", "");
1258
1259 var customAttributes = {};
1260
1261 var embedInnerWrapper = $('.embedpress-wrapper', $wrapper);
1262 var embedItem = $('iframe', $wrapper);
1263 if (!embedItem.length) {
1264 embedItem = null;
1265 }
1266
1267 $.each(embedInnerWrapper.attributes, function() {
1268 if (this.specified) {
1269 if (this.name !== "class") {
1270 customAttributes[this.name.replace('data-', "").toLowerCase()] = this.value;
1271 }
1272 }
1273 });
1274
1275 var embedWidth = (((embedItem && embedItem.width()) || embedInnerWrapper.data('width')) || embedInnerWrapper.width()) || "";
1276 var embedHeight = (((embedItem && embedItem.height()) || embedInnerWrapper.data('height')) || embedInnerWrapper.height()) || "";
1277
1278 embedItem = embedInnerWrapper = null;
1279
1280 $('<div class="loader-indicator"><i class="embedpress-icon-reload"></i></div>').appendTo($wrapper);
1281
1282 setTimeout(function() {
1283 $.ajax({
1284 type: "GET",
1285 url: self.params.baseUrl +"wp-admin/admin-ajax.php",
1286 data: {
1287 action: "embedpress_get_embed_url_info",
1288 url: self.decodeEmbedURLSpecialChars($wrapper.data('url'), false)
1289 },
1290 beforeSend: function(request, requestSettings) {
1291 $('.loader-indicator', $wrapper).addClass('is-loading');
1292 },
1293 success: function(response) {
1294 if (!response) {
1295 bootbox.alert('Unable to get a valid response from the server.');
1296 return;
1297 }
1298
1299 if (response.canBeResponsive) {
1300 var embedShouldBeResponsive = true;
1301 if ("width" in customAttributes || "height" in customAttributes) {
1302 embedShouldBeResponsive = false;
1303 } else if ("responsive" in customAttributes && customAttributes['responsive'].isFalse()) {
1304 embedShouldBeResponsive = false;
1305 }
1306 }
1307
1308 bootbox.dialog({
1309 className: "embedpress-modal",
1310 title: "Editing Embed properties",
1311 message: '<form id="form-'+ wrapperUid +'" embedpress>'+
1312 '<div class="row">'+
1313 '<div class="col-md-12">'+
1314 '<div class="form-group">'+
1315 '<label for="input-url-'+ wrapperUid +'">Url</label>'+
1316 '<input class="form-control" type="url" id="input-url-'+ wrapperUid +'" value="'+ self.decodeEmbedURLSpecialChars($wrapper.data('url'), false) +'">'+
1317 '</div>'+
1318 '</div>'+
1319 '</div>'+
1320 '<div class="row">'+
1321 (response.canBeResponsive ?
1322 '<div class="col-md-12">'+
1323 '<label>Responsive</label>'+
1324 '<div class="form-group">'+
1325 '<label class="radio-inline">'+
1326 '<input type="radio" name="input-responsive-'+ wrapperUid +'" id="input-responsive-1-'+ wrapperUid +'" value="1"'+ (embedShouldBeResponsive ? ' checked' : '') +'> Yes'+
1327 '</label>'+
1328 '<label class="radio-inline">'+
1329 '<input type="radio" name="input-responsive-'+ wrapperUid +'" id="input-responsive-0-'+ wrapperUid +'" value="0"'+ (!embedShouldBeResponsive ? ' checked' : '') +'> No'+
1330 '</label>'+
1331 '</div>'+
1332 '</div>' : '')+
1333 '<div class="col-md-6">'+
1334 '<div class="form-group">'+
1335 '<label for="input-width-'+ wrapperUid +'">Width</label>'+
1336 '<input class="form-control" type="integer" id="input-width-'+ wrapperUid +'" value="'+ embedWidth +'"'+ (embedShouldBeResponsive ? ' disabled' : '') +'>'+
1337 '</div>'+
1338 '</div>'+
1339 '<div class="col-md-6">'+
1340 '<div class="form-group">'+
1341 '<label for="input-height-'+ wrapperUid +'">Height</label>'+
1342 '<input class="form-control" type="integer" id="input-height-'+ wrapperUid +'" value="'+ embedHeight +'"'+ (embedShouldBeResponsive ? ' disabled' : '') +'>'+
1343 '</div>'+
1344 '</div>'+
1345 '</div>'+
1346 '</form>',
1347 buttons: {
1348 danger: {
1349 label: "Cancel",
1350 className: "btn-default",
1351 callback: function() {
1352 // do nothing
1353 self.activeWrapperForModal = null;
1354 }
1355 },
1356 success: {
1357 label: "Save",
1358 className: "btn-primary",
1359 callback: function() {
1360 var $wrapper = self.activeWrapperForModal;
1361
1362 // Select the current wrapper as a base for the new element
1363 editorInstance.focus();
1364 editorInstance.selection.select($wrapper[0]);
1365
1366 $wrapper.children().remove();
1367 $wrapper.remove();
1368
1369 if (response.canBeResponsive) {
1370 if ($('#form-'+ wrapperUid +' input[name="input-responsive-'+ wrapperUid +'"]:checked').val().isFalse()) {
1371 var embedCustomWidth = $('#input-width-'+ wrapperUid).val();
1372 if (parseInt(embedCustomWidth) > 0) {
1373 customAttributes['width'] = embedCustomWidth;
1374 }
1375
1376 var embedCustomHeight = $('#input-height-'+ wrapperUid).val();
1377 if (parseInt(embedCustomHeight) > 0) {
1378 customAttributes['height'] = embedCustomHeight;
1379 }
1380
1381 customAttributes['responsive'] = "false";
1382 } else {
1383 delete customAttributes['width'];
1384 delete customAttributes['height'];
1385
1386 customAttributes['responsive'] = "true";
1387 }
1388 } else {
1389 var embedCustomWidth = $('#input-width-'+ wrapperUid).val();
1390 if (parseInt(embedCustomWidth) > 0) {
1391 customAttributes['width'] = embedCustomWidth;
1392 }
1393
1394 var embedCustomHeight = $('#input-height-'+ wrapperUid).val();
1395 if (parseInt(embedCustomHeight) > 0) {
1396 customAttributes['height'] = embedCustomHeight;
1397 }
1398 }
1399
1400 var customAttributesList = [];
1401 if (!!Object.keys(customAttributes).length) {
1402 for (var attrName in customAttributes) {
1403 customAttributesList.push(attrName + '="' + customAttributes[attrName] + '"');
1404 }
1405 }
1406
1407 var shortcode = '['+ $data.EMBEDPRESS_SHORTCODE + (customAttributesList.length > 0 ? " "+ customAttributesList.join(" ") : "") +']'+ $('#input-url-'+ wrapperUid).val() +'[/'+ $data.EMBEDPRESS_SHORTCODE +']';
1408 // We do not directly replace the node because it was causing a bug on a second edit attempt
1409 editorInstance.execCommand('mceInsertContent', false, shortcode);
1410
1411 self.configureWrappers(editorInstance);
1412 }
1413 }
1414 }
1415 });
1416
1417 $('form[embedpress]').on('change', 'input[type="radio"]', function(e) {
1418 var self = $(this);
1419 var form = self.parents('form[embedpress]');
1420
1421 $('input[type="integer"]', form).prop('disabled', self.val().isTrue());
1422 });
1423 },
1424 complete: function(request, textStatus) {
1425 $('.loader-indicator', $wrapper).removeClass('is-loading');
1426
1427 setTimeout(function() {
1428 $('.loader-indicator', $wrapper).remove();
1429 }, 350);
1430 },
1431 dataType: "json",
1432 async: true
1433 });
1434 }, 200);
1435
1436 return false;
1437 };
1438
1439 /**
1440 * Method executed when the remove button is clicked. It will remove
1441 * the preview and embed code, adding a mark to ignore the url
1442 *
1443 * @param Object e The event
1444 * @return void
1445 */
1446 self.onClickRemoveButton = function(e, editorInstance) {
1447 // Prevent edition of the panel
1448 self.cancelEvent(e, editorInstance);
1449
1450 var $wrapper = self.activeWrapper;
1451
1452 $wrapper.children().remove();
1453 $wrapper.remove();
1454
1455 return false;
1456 };
1457
1458 self.recursivelyAddClass = function(element, className) {
1459 $(element).children().each(function(index, child) {
1460 $(child).addClass(className);
1461
1462 var grandChild = $(child).children();
1463 if (grandChild.length > 0) {
1464 self.recursivelyAddClass(child, className)
1465 }
1466 });
1467 };
1468
1469 self.setInterval = function(callback, time, timeout) {
1470 var elapsed = 0;
1471 var iteraction = 0;
1472
1473 var interval = window.setInterval(function() {
1474 elapsed += time;
1475 iteraction++;
1476
1477 if (elapsed <= timeout) {
1478 callback(iteraction, elapsed);
1479 } else {
1480 self.stopInterval(interval);
1481 }
1482 }, time);
1483
1484 return interval;
1485 };
1486
1487 self.stopInterval = function(interval) {
1488 window.clearInterval(interval);
1489 interval = null;
1490 };
1491
1492 /**
1493 * Configure unconfigured embed wrappers, adding events and css
1494 * @return void
1495 */
1496 self.configureWrappers = function(editorInstance) {
1497 window.setTimeout(
1498 function configureWrappersTimeOut() {
1499 var doc = editorInstance.getDoc(),
1500 total = 0,
1501 $wrapper = null,
1502 $iframe = null;
1503
1504 // Get all the wrappers
1505 var wrappers = doc.getElementsByClassName('embedpress_wrapper');
1506 total = wrappers.length;
1507 if (total > 0) {
1508 for (var i = 0; i < total; i++) {
1509 $wrapper = $(wrappers[i]);
1510
1511 // Check if the wrapper wasn't already configured
1512 if ($wrapper.data('configured') != true) {
1513 // A timeout was set to avoid block the content loading
1514 window.setTimeout(function() {
1515 // @todo: Check if we need a limit of levels to avoid use too much resources
1516 self.recursivelyAddClass($wrapper, 'embedpress_ignore_mouseout');
1517 }, 500);
1518
1519 // Fix the wrapper size. Wait until find the child iframe. L
1520 var interval = self.setInterval(function(iteraction) {
1521 var $childIframes = $wrapper.find('iframe');
1522
1523 if ($childIframes.length > 0) {
1524 $.each($childIframes, function(index, iframe) {
1525 // Facebook has more than one iframe, we need to ignore the Cross Domain Iframes
1526 if ($(iframe).attr('id') !== 'fb_xdm_frame_https'
1527 && $(iframe).attr('id') !== 'fb_xdm_frame_http'
1528 ) {
1529 $wrapper.css('width', $(iframe).width() + 'px');
1530 self.stopInterval(interval);
1531 }
1532 });
1533 }
1534 }, 500, 8000);
1535
1536 $wrapper.data('configured', true);
1537 }
1538 }
1539 }
1540 },
1541 200
1542 );
1543 };
1544
1545 /**
1546 * Hide the controller panel
1547 *
1548 * @return void
1549 */
1550 self.hidePreviewControllerPanel = function() {
1551 if (self.controllerPanelIsActive()) {
1552 $(self.activeControllerPanel).addClass('hidden');
1553 self.activeControllerPanel = null;
1554 self.activeWrapper = null;
1555 }
1556 };
1557
1558 /**
1559 * Get an element by id in the editor's content
1560 *
1561 * @param String id The element id
1562 * @return Element The found element or null, wrapped by jQuery
1563 */
1564 self.getElementInContentById = function(id, editorInstance) {
1565 var doc = editorInstance.getDoc();
1566
1567 return $(doc.getElementById(id));
1568 };
1569
1570 /**
1571 * Show the controller panel
1572 *
1573 * @param element $wrapper The wrapper which will be activate
1574 * @return void
1575 */
1576 self.displayPreviewControllerPanel = function($wrapper, editorInstance) {
1577 if (self.controllerPanelIsActive() && $wrapper !== self.activeWrapper) {
1578 self.hidePreviewControllerPanel();
1579 }
1580
1581 if (!self.controllerPanelIsActive() && !$wrapper.hasClass('is-loading')) {
1582 var uid = $wrapper.data('uid');
1583 var $panel = self.getElementInContentById('embedpress_controller_panel_' + uid, editorInstance);
1584
1585 if (!$panel.data('event-set')) {
1586 var $editButton = self.getElementInContentById('embedpress_button_edit_' + uid, editorInstance);
1587 var $removeButton = self.getElementInContentById('embedpress_button_remove_' + uid, editorInstance);
1588
1589 self.addEvent('mousedown', $editButton, function(e) {
1590 self.onClickEditButton(e, editorInstance);
1591 });
1592
1593 self.addEvent('mousedown', $removeButton, function(e) {
1594 self.onClickRemoveButton(e, editorInstance);
1595 });
1596
1597 $panel.data('event-set', true);
1598 }
1599
1600 // Update the position of the control bar
1601 var next = $panel.next()[0];
1602 if (typeof next !== 'undefined') {
1603 if (next.nodeName.toLowerCase() === 'iframe') {
1604 $panel.css('left', ($(next).width() / 2));
1605 }
1606 }
1607
1608 // Show the bar
1609 $panel.removeClass('hidden');
1610
1611 self.activeControllerPanel = $panel;
1612 self.activeWrapper = $wrapper;
1613 }
1614 };
1615 };
1616
1617 if (!window.EmbedPress) {
1618 window.EmbedPress = new EmbedPress();
1619 }
1620
1621 window.EmbedPress.init($data.previewSettings);
1622 });
1623 })(jQuery, String, $data);
1624