PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.10
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.10
2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.5.23 All 138 releases
metasync / admin / js / metasync-opengraph.js

metasync-opengraph.js in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.10, at admin/js/metasync-opengraph.js

568 lines 17.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * MetaSync Open Graph Admin JavaScript
3 *
4 * @package MetaSync
5 * @subpackage MetaSync/admin/js
6 * @since 1.0.0
7 */
8
9 (function ($) {
10 'use strict';
11
12 // Check if required dependencies are available
13 if (typeof $ === 'undefined') {
14 return;
15 }
16
17 if (typeof metasync_og === 'undefined') {
18 return;
19 }
20
21 /**
22 * Open Graph Meta Box functionality
23 */
24 var MetaSyncOpenGraph = {
25
26 initialized: false,
27
28 /**
29 * Initialize the functionality
30 */
31 init: function () {
32 if (this.initialized) {
33 return;
34 }
35
36 this.bindEvents();
37 this.updateCharacterCounts();
38 this.generateInitialPreview();
39 this.handleUrlFieldState();
40 this.updateUrlFieldOnStatusChange();
41 this.handleUsePermalinkClick();
42 this.initializeTwitterCardSections();
43 this.initialized = true;
44 },
45
46 /**
47 * Bind event handlers
48 */
49 bindEvents: function () {
50 var self = this;
51
52 // Toggle Open Graph section
53 $(document).on('change', '.metasync-og-toggle', function () {
54 var $content = $('.metasync-og-content');
55 if ($(this).is(':checked')) {
56 $content.slideDown();
57 self.generatePreview();
58 } else {
59 $content.slideUp();
60 }
61 });
62
63 // Toggle Twitter Card sections based on card type
64 $(document).on('change', '#metasync_twitter_card', function () {
65 var cardType = $(this).val();
66 var $appSection = $('.metasync-twitter-app-section');
67 var $playerSection = $('.metasync-twitter-player-section');
68
69 // Hide all sections first
70 // $appSection.hide();
71 // $playerSection.hide();
72 // Hide all sections first and disable their inputs
73 $appSection.hide().find('input, select, textarea').prop('disabled', true);
74 $playerSection.hide().find('input, select, textarea').prop('disabled', true);
75 // Show relevant section based on card type
76 if (cardType === 'app') {
77 // $appSection.slideDown();
78 $appSection.slideDown().find('input, select, textarea').prop('disabled', false);
79 } else if (cardType === 'player') {
80 // $playerSection.slideDown();
81 $playerSection.slideDown().find('input, select, textarea').prop('disabled', false);
82 }
83 });
84
85 // Preview tab switching
86 $(document).on('click', '.metasync-preview-tab', function (e) {
87 e.preventDefault();
88 var platform = $(this).data('platform');
89
90 // Update tabs
91 $('.metasync-preview-tab').removeClass('active');
92 $(this).addClass('active');
93
94 // Update panels
95 $('.metasync-preview-panel').removeClass('active');
96 $('.metasync-preview-panel[data-platform="' + platform + '"]').addClass('active');
97
98 });
99
100 // Real-time updates as user types (shorter debounce)
101 $(document).on('input keyup paste', '.metasync-og-input', function () {
102 self.updateCharacterCount($(this));
103 self.updatePreviewInstantly($(this));
104 });
105
106 // Image upload for Open Graph
107 $(document).on('click', '.metasync-upload-image', function (e) {
108 e.preventDefault();
109 self.openMediaUploader($(this), '#metasync_og_image', '.metasync-image-preview', '.metasync-remove-image');
110 });
111
112 // Image upload for Twitter
113 $(document).on('click', '.metasync-upload-twitter-image', function (e) {
114 e.preventDefault();
115 self.openMediaUploader($(this), '#metasync_twitter_image', '.metasync-twitter-image-preview', '.metasync-remove-twitter-image');
116 });
117
118 // Remove Open Graph image
119 $(document).on('click', '.metasync-remove-image', function (e) {
120 e.preventDefault();
121 self.removeImage('#metasync_og_image', '.metasync-image-preview', '.metasync-remove-image');
122 });
123
124 // Remove Twitter image
125 $(document).on('click', '.metasync-remove-twitter-image', function (e) {
126 e.preventDefault();
127 self.removeImage('#metasync_twitter_image', '.metasync-twitter-image-preview', '.metasync-remove-twitter-image');
128 });
129
130 // Manual preview refresh
131 $(document).on('click', '.metasync-refresh-preview', function (e) {
132 e.preventDefault();
133 self.generatePreview();
134 });
135
136 // Auto-sync Twitter fields with Open Graph
137 $(document).on('input', '#metasync_og_title', function () {
138 var twitterTitle = $('#metasync_twitter_title');
139 if (twitterTitle.val() === '' || twitterTitle.data('auto-synced')) {
140 twitterTitle.val($(this).val()).data('auto-synced', true);
141 self.updateCharacterCount(twitterTitle);
142 }
143 });
144
145 $(document).on('input', '#metasync_og_description', function () {
146 var twitterDesc = $('#metasync_twitter_description');
147 if (twitterDesc.val() === '' || twitterDesc.data('auto-synced')) {
148 twitterDesc.val($(this).val()).data('auto-synced', true);
149 self.updateCharacterCount(twitterDesc);
150 }
151 });
152
153 $(document).on('input', '#metasync_og_image', function () {
154 var twitterImage = $('#metasync_twitter_image');
155 if (twitterImage.val() === '' || twitterImage.data('auto-synced')) {
156 twitterImage.val($(this).val()).data('auto-synced', true);
157 self.updateTwitterImagePreview();
158 }
159 });
160
161 // Manual input breaks auto-sync
162 $(document).on('input', '#metasync_twitter_title, #metasync_twitter_description, #metasync_twitter_image', function () {
163 $(this).data('auto-synced', false);
164 });
165 },
166
167 /**
168 * Update character count for an input
169 */
170 updateCharacterCount: function ($input) {
171 var maxLength = parseInt($input.attr('maxlength')) || 0;
172 var currentLength = $input.val().length;
173 var $counter = $('.metasync-char-count[data-target="' + $input.attr('id') + '"]');
174
175 if ($counter.length && maxLength > 0) {
176 $counter.text(currentLength + '/' + maxLength);
177
178 // Add warning class if approaching limit
179 if (currentLength > maxLength * 0.9) {
180 $counter.addClass('metasync-char-warning');
181 } else {
182 $counter.removeClass('metasync-char-warning');
183 }
184 }
185 },
186
187 /**
188 * Update all character counts
189 */
190 updateCharacterCounts: function () {
191 var self = this;
192 $('.metasync-og-input[maxlength]').each(function () {
193 self.updateCharacterCount($(this));
194 });
195 },
196
197 /**
198 * Open WordPress media uploader
199 */
200 openMediaUploader: function ($button, inputSelector, previewSelector, removeButtonSelector) {
201 var self = this;
202
203
204 // Check if wp.media is available
205 if (typeof wp === 'undefined' || typeof wp.media === 'undefined') {
206 alert('WordPress media library is not available. Please refresh the page.');
207 return;
208 }
209
210 // Create media frame
211 var frame = wp.media({
212 title: metasync_og.strings.select_image,
213 button: {
214 text: metasync_og.strings.use_image
215 },
216 multiple: false,
217 library: {
218 type: 'image'
219 }
220 });
221
222 // Handle image selection
223 frame.on('select', function () {
224 var attachment = frame.state().get('selection').first().toJSON();
225 var imageUrl = attachment.sizes.large ? attachment.sizes.large.url : attachment.url;
226
227 $(inputSelector).val(imageUrl);
228 $(previewSelector + ' img').attr('src', imageUrl);
229 $(previewSelector).show();
230 $(removeButtonSelector).show();
231
232 // Update preview instantly if it's the main OG image
233 if (inputSelector === '#metasync_og_image') {
234 self.updatePreviewImages(imageUrl);
235 }
236
237 self.debouncePreview();
238 });
239
240 // Open the frame
241 frame.open();
242 },
243
244 /**
245 * Remove image
246 */
247 removeImage: function (inputSelector, previewSelector, removeButtonSelector) {
248 $(inputSelector).val('');
249 $(previewSelector).hide();
250 $(removeButtonSelector).hide();
251
252 // Update preview instantly if it's the main OG image
253 if (inputSelector === '#metasync_og_image') {
254 this.updatePreviewImages('');
255 }
256
257 this.debouncePreview();
258 },
259
260 /**
261 * Update Twitter image preview
262 */
263 updateTwitterImagePreview: function () {
264 var imageUrl = $('#metasync_twitter_image').val();
265 var $preview = $('.metasync-twitter-image-preview');
266 var $removeBtn = $('.metasync-remove-twitter-image');
267
268 if (imageUrl) {
269 $preview.find('img').attr('src', imageUrl);
270 $preview.show();
271 $removeBtn.show();
272 } else {
273 $preview.hide();
274 $removeBtn.hide();
275 }
276 },
277
278 /**
279 * Update preview instantly without AJAX for better UX
280 */
281 updatePreviewInstantly: function ($input) {
282 if (!$('.metasync-og-toggle').is(':checked')) {
283 return;
284 }
285
286 var inputId = $input.attr('id');
287 var value = $input.val();
288
289 // Update preview content immediately based on input
290 switch(inputId) {
291 case 'metasync_og_title':
292 $('.facebook-preview-title, .twitter-card-title, .linkedin-preview-title').text(value || 'Your Post Title');
293 break;
294 case 'metasync_og_description':
295 $('.facebook-preview-description, .twitter-card-description, .linkedin-preview-description').text(value || 'Your post description will appear here when shared on social media platforms.');
296 break;
297 case 'metasync_og_image':
298 this.updatePreviewImages(value);
299 break;
300 case 'metasync_og_url':
301 var domain = this.extractDomain(value);
302 $('.facebook-preview-domain').text(domain.toUpperCase());
303 $('.twitter-card-domain, .linkedin-preview-domain').text(domain);
304 break;
305 }
306
307 // Also debounce the full AJAX update
308 this.debouncePreview();
309 },
310
311 /**
312 * Extract domain from URL
313 */
314 extractDomain: function (url) {
315 if (!url) {
316 return window.location.hostname || 'your-site.com';
317 }
318
319 try {
320 var a = document.createElement('a');
321 a.href = url;
322 return a.hostname || 'your-site.com';
323 } catch(e) {
324 return 'your-site.com';
325 }
326 },
327
328 /**
329 * Update preview images instantly
330 */
331 updatePreviewImages: function (imageUrl) {
332 var $images = $('.facebook-preview-image, .twitter-card-image, .linkedin-preview-image');
333
334 if (imageUrl) {
335 $images.each(function () {
336 var $container = $(this);
337 $container.removeClass('preview-no-image');
338 $container.html('<img src="' + imageUrl + '" alt="Preview" onerror="this.parentElement.classList.add(\'preview-no-image\'); this.style.display=\'none\'; this.parentElement.innerHTML=\'<div class=\\\"preview-placeholder\\\"><span>📷</span><p>Image failed to load</p></div>\';">');
339 });
340 } else {
341 $images.each(function () {
342 var $container = $(this);
343 $container.addClass('preview-no-image');
344 $container.html('<div class="preview-placeholder"><span>📷</span><p>No image selected</p></div>');
345 });
346 }
347 },
348
349 /**
350 * Debounced preview generation
351 */
352 debouncePreview: function () {
353 var self = this;
354 clearTimeout(this.previewTimeout);
355 this.previewTimeout = setTimeout(function () {
356 self.generatePreview();
357 }, 1000); // Longer timeout since we have instant updates
358 },
359
360 /**
361 * Generate initial preview on page load
362 */
363 generateInitialPreview: function () {
364 // Only generate if toggle is checked and we don't already have a preview
365 if ($('.metasync-og-toggle').is(':checked')) {
366 var $content = $('.metasync-preview-content-wrapper');
367 if ($content.is(':empty') || $content.find('.metasync-preview-tabs').length === 0) {
368 this.generatePreview();
369 }
370 }
371 },
372
373 /**
374 * Generate social media preview
375 */
376 generatePreview: function () {
377 if (!$('.metasync-og-toggle').is(':checked')) {
378 return;
379 }
380
381 var $container = $('.metasync-preview-content-wrapper');
382 var $loading = $('.metasync-preview-loading');
383
384 // Get current values
385 var title = $('#metasync_og_title').val() || $('#title').val() || $('h1.wp-heading-inline').text() || '';
386 var description = $('#metasync_og_description').val() || '';
387 var image = $('#metasync_og_image').val() || '';
388 var url = $('#metasync_og_url').val() || (typeof metasync_og !== 'undefined' && metasync_og.current_permalink) || window.location.href;
389
390 // Get Twitter Card data
391 var twitter_title = $('#metasync_twitter_title').val() || '';
392 var twitter_description = $('#metasync_twitter_description').val() || '';
393 var twitter_image = $('#metasync_twitter_image').val() || '';
394
395 // Show loading state
396 $loading.show();
397 $container.hide();
398
399 // Make AJAX request
400 $.ajax({
401 url: metasync_og.ajax_url,
402 type: 'POST',
403 data: {
404 action: 'metasync_og_preview',
405 nonce: metasync_og.nonce,
406 title: title,
407 description: description,
408 image: image,
409 url: url,
410 twitter_title: twitter_title,
411 twitter_description: twitter_description,
412 twitter_image: twitter_image
413 },
414 success: function (response) {
415 if (response.success) {
416 $container.empty().show();
417 var parser = new DOMParser();
418 var doc = parser.parseFromString(response.data.preview || '', 'text/html');
419 while (doc.body.firstChild) {
420 $container[0].appendChild(doc.body.firstChild);
421 }
422 } else {
423 $container.empty().append(
424 $('<p>').addClass('metasync-error').text('Failed to generate preview: ' + (response.data || 'Unknown error'))
425 ).show();
426 }
427 },
428 error: function (xhr, status, error) {
429 $container.empty().append(
430 $('<p>').addClass('metasync-error').text('AJAX Error: ' + error)
431 ).show();
432 },
433 complete: function () {
434 $loading.hide();
435 }
436 });
437 },
438
439 /**
440 * Handle URL field state based on post status
441 */
442 handleUrlFieldState: function () {
443 var $urlField = $('#metasync_og_url');
444 var $urlNotice = $('.metasync-url-disabled-notice');
445 var $usePermalinkBtn = $('.metasync-use-permalink');
446
447 if ($urlField.length) {
448 // Check if this is a new post (auto-draft only)
449 var isNewPost = $urlField.prop('disabled');
450
451 if (isNewPost) {
452 // For new posts (auto-draft), disable the field and show notice
453 $urlField.prop('disabled', true);
454 if ($urlNotice.length === 0) {
455 $urlField.after('<p class="description metasync-url-disabled-notice"><span class="dashicons dashicons-info"></span>This field will be automatically populated with the post permalink after you save the post for the first time.</p>');
456 }
457 $usePermalinkBtn.hide();
458 } else {
459 // For existing posts (draft, published, etc.), enable the field and show permalink button
460 $urlField.prop('disabled', false);
461 $urlNotice.remove();
462 $usePermalinkBtn.show();
463 }
464 }
465 },
466
467 /**
468 * Update URL field when post status changes
469 */
470 updateUrlFieldOnStatusChange: function () {
471 var self = this;
472
473 // Listen for post status changes (when user clicks Save Draft, Publish, etc.)
474 $(document).on('click', '#publish, #save-post, #save-post-ajax', function () {
475 // Small delay to allow WordPress to process the status change
476 setTimeout(function () {
477 self.handleUrlFieldState();
478 }, 500);
479 });
480
481 // Also listen for form submission
482 $(document).on('submit', '#post', function () {
483 setTimeout(function () {
484 self.handleUrlFieldState();
485 }, 1000);
486 });
487 },
488
489 /**
490 * Handle "Use Post Permalink" button click
491 */
492 handleUsePermalinkClick: function () {
493 var self = this;
494
495 $(document).on('click', '.metasync-use-permalink', function (e) {
496 e.preventDefault();
497
498 // Get the current permalink from the placeholder or generate it
499 var $urlField = $('#metasync_og_url');
500 var currentPermalink = $urlField.attr('placeholder');
501
502 // If no placeholder, try to get it from the localized data
503 if (!currentPermalink && typeof metasync_og !== 'undefined' && metasync_og.current_permalink) {
504 currentPermalink = metasync_og.current_permalink;
505 }
506
507 if (currentPermalink) {
508 $urlField.val(currentPermalink);
509 // Trigger change event to update preview
510 $urlField.trigger('change');
511 }
512 });
513 },
514
515 /**
516 * Initialize Twitter Card sections based on current card type
517 */
518 initializeTwitterCardSections: function () {
519 var cardType = $('#metasync_twitter_card').val();
520 var $appSection = $('.metasync-twitter-app-section');
521 var $playerSection = $('.metasync-twitter-player-section');
522
523 // Hide all sections first
524 // $appSection.hide();
525 // $playerSection.hide();
526 // Hide all sections first and disable their inputs
527 $appSection.hide().find('input, select, textarea').prop('disabled', true);
528 $playerSection.hide().find('input, select, textarea').prop('disabled', true);
529 // Show relevant section based on current card type
530 if (cardType === 'app') {
531 $appSection.show();
532 $appSection.show().find('input, select, textarea').prop('disabled', false);
533 } else if (cardType === 'player') {
534 // $playerSection.show();
535 $playerSection.show().find('input, select, textarea').prop('disabled', false);
536 }
537 }
538
539
540 };
541
542
543 /**
544 * Initialize when document is ready
545 */
546 $(document).ready(function () {
547 if ($('.metasync-opengraph-meta-box').length) {
548 MetaSyncOpenGraph.init();
549 } else {
550 // Try again after a delay in case the meta box is loaded dynamically
551 setTimeout(function () {
552 if ($('.metasync-opengraph-meta-box').length) {
553 MetaSyncOpenGraph.init();
554 } else {
555 }
556 }, 1000);
557 }
558 });
559
560 // Also try to initialize on window load as a fallback
561 $(window).on('load', function () {
562 if ($('.metasync-opengraph-meta-box').length && !MetaSyncOpenGraph.initialized) {
563 MetaSyncOpenGraph.init();
564 }
565 });
566
567 })(jQuery);
568