PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.5
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.5
2.7.1 2.7.2 2.7.0 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 All 141 releases
metasync / admin / js / metasync-opengraph.js

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

559 lines 17.1 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.html(response.data.preview).show();
417 } else {
418 $container.html('<p class="metasync-error">Failed to generate preview: ' + (response.data || 'Unknown error') + '</p>').show();
419 }
420 },
421 error: function (xhr, status, error) {
422 $container.html('<p class="metasync-error">AJAX Error: ' + error + '</p>').show();
423 },
424 complete: function () {
425 $loading.hide();
426 }
427 });
428 },
429
430 /**
431 * Handle URL field state based on post status
432 */
433 handleUrlFieldState: function () {
434 var $urlField = $('#metasync_og_url');
435 var $urlNotice = $('.metasync-url-disabled-notice');
436 var $usePermalinkBtn = $('.metasync-use-permalink');
437
438 if ($urlField.length) {
439 // Check if this is a new post (auto-draft only)
440 var isNewPost = $urlField.prop('disabled');
441
442 if (isNewPost) {
443 // For new posts (auto-draft), disable the field and show notice
444 $urlField.prop('disabled', true);
445 if ($urlNotice.length === 0) {
446 $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>');
447 }
448 $usePermalinkBtn.hide();
449 } else {
450 // For existing posts (draft, published, etc.), enable the field and show permalink button
451 $urlField.prop('disabled', false);
452 $urlNotice.remove();
453 $usePermalinkBtn.show();
454 }
455 }
456 },
457
458 /**
459 * Update URL field when post status changes
460 */
461 updateUrlFieldOnStatusChange: function () {
462 var self = this;
463
464 // Listen for post status changes (when user clicks Save Draft, Publish, etc.)
465 $(document).on('click', '#publish, #save-post, #save-post-ajax', function () {
466 // Small delay to allow WordPress to process the status change
467 setTimeout(function () {
468 self.handleUrlFieldState();
469 }, 500);
470 });
471
472 // Also listen for form submission
473 $(document).on('submit', '#post', function () {
474 setTimeout(function () {
475 self.handleUrlFieldState();
476 }, 1000);
477 });
478 },
479
480 /**
481 * Handle "Use Post Permalink" button click
482 */
483 handleUsePermalinkClick: function () {
484 var self = this;
485
486 $(document).on('click', '.metasync-use-permalink', function (e) {
487 e.preventDefault();
488
489 // Get the current permalink from the placeholder or generate it
490 var $urlField = $('#metasync_og_url');
491 var currentPermalink = $urlField.attr('placeholder');
492
493 // If no placeholder, try to get it from the localized data
494 if (!currentPermalink && typeof metasync_og !== 'undefined' && metasync_og.current_permalink) {
495 currentPermalink = metasync_og.current_permalink;
496 }
497
498 if (currentPermalink) {
499 $urlField.val(currentPermalink);
500 // Trigger change event to update preview
501 $urlField.trigger('change');
502 }
503 });
504 },
505
506 /**
507 * Initialize Twitter Card sections based on current card type
508 */
509 initializeTwitterCardSections: function () {
510 var cardType = $('#metasync_twitter_card').val();
511 var $appSection = $('.metasync-twitter-app-section');
512 var $playerSection = $('.metasync-twitter-player-section');
513
514 // Hide all sections first
515 // $appSection.hide();
516 // $playerSection.hide();
517 // Hide all sections first and disable their inputs
518 $appSection.hide().find('input, select, textarea').prop('disabled', true);
519 $playerSection.hide().find('input, select, textarea').prop('disabled', true);
520 // Show relevant section based on current card type
521 if (cardType === 'app') {
522 $appSection.show();
523 $appSection.show().find('input, select, textarea').prop('disabled', false);
524 } else if (cardType === 'player') {
525 // $playerSection.show();
526 $playerSection.show().find('input, select, textarea').prop('disabled', false);
527 }
528 }
529
530
531 };
532
533
534 /**
535 * Initialize when document is ready
536 */
537 $(document).ready(function () {
538 if ($('.metasync-opengraph-meta-box').length) {
539 MetaSyncOpenGraph.init();
540 } else {
541 // Try again after a delay in case the meta box is loaded dynamically
542 setTimeout(function () {
543 if ($('.metasync-opengraph-meta-box').length) {
544 MetaSyncOpenGraph.init();
545 } else {
546 }
547 }, 1000);
548 }
549 });
550
551 // Also try to initialize on window load as a fallback
552 $(window).on('load', function () {
553 if ($('.metasync-opengraph-meta-box').length && !MetaSyncOpenGraph.initialized) {
554 MetaSyncOpenGraph.init();
555 }
556 });
557
558 })(jQuery);
559