PluginProbe
Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels / 2.6.4
Automatic YouTube Gallery – Embed Auto-Updating YouTube Video Galleries, Feeds, Playlists & Channels v2.6.4
2.9.1 2.9.0 trunk 1.0.0 1.1.0 1.2.0 1.3.0 1.4.0 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 2.0.0 2.1.0 2.2.0 2.3.2 2.3.3 2.3.5 2.3.6 2.3.8 2.3.9 2.4.3 All 37 releases
← All changes | admin/assets/js/admin.js +208 -401 2.9.0 → 2.6.4 View file →
@@ -5,10 +5,10 @@
5 5 * Display the media uploader.
6 6 *
7 7 * @since 1.0.0
8 8 */
9 - function renderMediaUploader( $elem ) {
10 - let file_frame, attachment;
9 + function ayg_render_media_uploader( $elem ) {
10 + var file_frame, attachment;
11 11
12 12 // If an instance of file_frame already exists, then we can open it rather than creating a new instance
13 13 if ( file_frame ) {
14 14 file_frame.open();
@@ -32,9 +32,10 @@
32 32 return;
33 33 };
34 34
35 35 // Set the data
36 - $elem.prev( '.ayg-settings-url' ).val( attachment.url );
36 + $elem.prev( '.ayg-settings-url' )
37 + .val( attachment.url );
37 38 });
38 39
39 40 // Now display the actual file_frame
40 41 file_frame.open();
@@ -40,62 +41,84 @@
40 41 file_frame.open();
41 42 };
42 43
43 44 /**
44 - * Widget: Initiate color picker
45 + * Get Youtube playlist ID from Youtube URL.
45 46 *
46 - * @since 1.0.0
47 + * @since 1.0.0
48 + * @param {string} url - URL from which to extract the ID.
49 + * @return {string}
47 50 */
48 - function initWidgetColorPicker( widget ) {
49 - widget.find( '.ayg-color-picker' ).wpColorPicker( {
50 - change: _.throttle( function() { // For Customizer
51 - $( this ).trigger( 'change' );
52 - }, 3000 )
53 - });
51 + function ayg_get_youtube_playlist_id( url ) {
52 + var id = /[&|\?]list=([a-zA-Z0-9_-]+)/gi.exec( url );
53 + return ( id && id.length > 0 ) ? id[1] : url;
54 54 }
55 55
56 - function onWidgetUpdate( event, widget ) {
57 - initWidgetColorPicker( widget );
56 + /**
57 + * Get Youtube channel ID from Youtube URL.
58 + *
59 + * @since 1.0.0
60 + * @param {string} url - URL from which to extract the ID.
61 + * @return {object}
62 + */
63 + function ayg_get_youtube_channel_id( url ) {
64 + var type = 'channel';
65 + var id = url;
66 +
67 + url = url.replace( /(>|<)/gi, '' ).split( /(\/channel\/|\/user\/)/ );
68 +
69 + if ( url[2] !== undefined ) {
70 + id = url[2].split( /[^0-9a-z_-]/i );
71 + id = id[0];
72 + }
73 +
74 + if ( /\/user\//.test( url ) ) {
75 + type = 'username';
76 + }
77 +
78 + return {
79 + type: type,
80 + id: id
81 + };
58 82 }
59 83
60 84 /**
61 - * Gallery Builder: Run the import batch loop for a gallery.
62 - *
63 - * Calls the ayg_import_gallery AJAX action repeatedly, passing back the
64 - * page token returned by the previous call, until the server reports done.
65 - *
66 - * @since 2.8.0
67 - */
68 - function importGallery( id, callbacks ) {
69 - const runBatch = function( pageToken ) {
70 - const data = {
71 - action: 'ayg_import_gallery',
72 - id: id,
73 - page_token: pageToken,
74 - security: ayg_admin.ajax_nonce
75 - };
85 + * Get Youtube video ID from Youtube URL.
86 + *
87 + * @since 1.0.0
88 + * @param {string} url - URL from which to extract the ID.
89 + * @return {string}
90 + */
91 + function ayg_get_youtube_video_id( url ) {
92 + var id = url;
76 93
77 - $.post( ajaxurl, data, function( response ) {
78 - if ( ! response.success ) {
79 - callbacks.error( response.data || {} );
80 - return;
81 - }
94 + url = url.replace( /(>|<)/gi, '' ).split( /(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/ );
82 95
83 - callbacks.progress( response.data );
96 + if ( url[2] !== undefined ) {
97 + id = url[2].split( /[^0-9a-z_\-]/i );
98 + id = id[0];
99 + }
84 100
85 - if ( response.data.done ) {
86 - callbacks.complete( response.data );
87 - } else {
88 - runBatch( response.data.next_page_token );
89 - }
90 - } ).fail( function() {
91 - callbacks.error( {} );
92 - } );
93 - };
101 + return id;
102 + }
94 103
95 - runBatch( '' );
104 + /**
105 + * Widget: Initiate color picker
106 + *
107 + * @since 1.0.0
108 + */
109 + function ayg_widget_color_picker( widget ) {
110 + widget.find( '.ayg-color-picker' ).wpColorPicker( {
111 + change: _.throttle( function() { // For Customizer
112 + $( this ).trigger( 'change' );
113 + }, 3000 )
114 + });
96 115 }
97 116
117 + function on_ayg_widget_update( event, widget ) {
118 + ayg_widget_color_picker( widget );
119 + }
120 +
98 121 /**
99 122 * Called when the page has loaded.
100 123 *
101 124 * @since 1.0.0
@@ -103,440 +126,224 @@
103 126 $(function() {
104 127 // Common: Initialize the color picker
105 128 $( '.ayg-color-picker' ).wpColorPicker();
106 129
107 - // Dashboard: Save API key
108 - $( '#ayg-button-save-api-key' ).on( 'click', function( event ) {
109 - event.preventDefault();
130 + // Common: Init the popup.
131 + if ( $.fn.magnificPopup ) {
132 + $( '.ayg-modal-button' ).magnificPopup({
133 + type: 'inline',
134 + mainClass: 'mfp-fade'
135 + });
136 + }
110 137
111 - const $button = $( this );
112 - const $field = $( '#ayg-form-field-api-key' );
113 - const $control = $field.closest( '.ayg-form-control' );
114 - const $status = $button.siblings( '.ayg-form-status' );
138 + // Dashboard: Save API Key
139 + $( '#ayg-button-save-api-key' ).on( 'click', function( e ) {
140 + e.preventDefault();
141 +
142 + $( this ).prop( 'disabled', true );
143 + $( '.ayg-ajax-status', '#ayg-table-api-key' ).html( '<span class="spinner"></span>' );
115 144
116 - const data = {
145 + var data = {
117 146 'action': 'ayg_save_api_key',
118 - 'api_key': $field.val(),
147 + 'api_key': $( '#ayg-api-key' ).val(),
119 148 'security': ayg_admin.ajax_nonce
120 149 };
121 150
122 - // Empty validation: Show the message inside the field control, then stop
123 - if ( ! $.trim( data.api_key ) ) {
124 - $control.addClass( 'ayg-form-invalid' );
125 - $control.find( '.ayg-form-status' ).html( '<span class="ayg-text-error"><span class="dashicons dashicons-dismiss" aria-hidden="true"></span>' + ayg_admin.i18n.invalid_api_key + '</span>' );
126 - $field.trigger( 'focus' );
127 - return;
151 + if ( ! data.api_key ) {
152 + $( this ).prop( 'disabled', false );
153 + $( '.ayg-ajax-status', '#ayg-table-api-key' ).html( '<span class="ayg-text-error">' + ayg_admin.i18n.invalid_api_key + '</span>' );
154 +
155 + return false;
128 156 }
129 -
130 - $control.removeClass( 'ayg-form-invalid' );
131 - $control.find( '.ayg-form-status' ).html( '' );
132 -
133 - // Disable the button and show a spinner while saving
134 - $button.prop( 'disabled', true ).prepend( '<span class="spinner"></span>' );
135 - $status.html( '' );
136 -
137 - // Perform AJAX request to save the API key
157 +
138 158 $.post( ajaxurl, data, function( response ) {
139 - if ( ! response.success ) {
140 - const message = ( response.data && response.data.message ) ? response.data.message : ayg_admin.i18n.save_failed;
141 -
142 - $button.prop( 'disabled', false ).find( '.spinner' ).remove();
143 - $status.html( '<span class="ayg-text-error"><span class="dashicons dashicons-dismiss" aria-hidden="true"></span>' + message + '</span>' );
144 - return;
145 - }
146 -
147 159 window.location.reload(); // Reload document
148 - } ).fail( function() {
149 - $button.prop( 'disabled', false ).find( '.spinner' ).remove();
150 - $status.html( '<span class="ayg-text-error"><span class="dashicons dashicons-dismiss" aria-hidden="true"></span>' + ayg_admin.i18n.save_failed + '</span>' );
151 - } );
160 + });
152 161 });
153 162
154 - // Dashboard: Clear the API key field error as the user types
155 - $( '#ayg-form-field-api-key' ).on( 'input', function() {
156 - const $control = $( this ).closest( '.ayg-form-control' );
163 + // Dashboard: Generate shortcode
164 + $( '#ayg-generate-shortcode' ).on( 'click', function( e ) {
165 + e.preventDefault();
157 166
158 - if ( $.trim( $( this ).val() ).length > 0 ) {
159 - $control.removeClass( 'ayg-form-invalid' );
160 - $control.find( '.ayg-form-status' ).html( '' );
161 - }
162 - });
167 + // Attributes
168 + var props = {};
169 + var popup = 0;
170 +
171 + // Loop through all editor fields
172 + $( '.ayg-editor-control', '#ayg-shortcode-builder' ).each(function() {
173 + var $elem = $( this ).find( '.ayg-editor-field' );
174 + var type = $elem.attr( 'type' );
175 + var key = $elem.attr( 'name' );
176 + var value = $elem.val();
177 + var def = $elem.data( 'default' );
178 +
179 + // Skip popup field
180 + if ( 'popup' == key ) {
181 + popup = $elem.is( ':checked' ) ? 1 : 0;
182 + return true; // continue
183 + }
163 184
164 - // Gallery Form: Warn before leaving with unsaved changes, or while an import is still running
165 - // (leaving mid-import leaves the gallery in a "running" state until cron resumes it).
166 - let form_dirty = false;
167 - let importing = false;
168 -
169 - $( '#ayg-gallery-form' ).on( 'change input', ':input', function() {
170 - form_dirty = true;
171 - } );
172 -
173 - $( window ).on( 'beforeunload', function( event ) {
174 - if ( form_dirty || importing ) {
175 - // Modern browsers show their own generic prompt and ignore a custom message
176 - // string; they require preventDefault() and/or returnValue to be set to fire it.
177 - event.preventDefault();
178 - ( event.originalEvent || event ).returnValue = ayg_admin.i18n.unsaved_changes;
179 - return ayg_admin.i18n.unsaved_changes;
180 - }
181 - } );
182 -
183 - // Gallery Form: Validate / clear a source field error as the user edits it.
184 - // The @handle pattern is deterministic, so the channel field (shared by channel
185 - // and livestream) re-checks it live; the empty check stays submit-only.
186 - $( '#ayg-gallery-form' ).on( 'input', '.ayg-form-section-source .ayg-form-field', function() {
187 - const $field = $( this );
188 - const $control = $field.closest( '.ayg-form-control' );
189 -
190 - if ( 'ayg-form-field-channel' === $field.attr( 'id' ) && $field.val().indexOf( '@' ) !== -1 ) {
191 - $control.addClass( 'ayg-form-invalid' );
192 -
193 - const $error = $( '<span class="ayg-text-error"><span class="dashicons dashicons-dismiss" aria-hidden="true"></span></span>' );
194 - $error.append( document.createTextNode( ayg_admin.i18n.channel_handle ) );
195 - $control.find( '.ayg-form-status' ).html( '' ).append( $error );
196 - return;
197 - }
198 -
199 - if ( $.trim( $( this ).val() ).length > 0 ) {
200 - $control.removeClass( 'ayg-form-invalid' );
201 - $control.find( '.ayg-form-status' ).html( '' );
202 - }
203 - } );
204 -
205 - // Gallery Form: Create / Update Gallery, then import videos inline
206 - $( '#ayg-button-save-gallery' ).on( 'click', function( event ) {
207 - event.preventDefault();
208 -
209 - const $button = $( this );
210 - const $form = $button.closest( 'form' );
211 - const $status = $button.siblings( '.ayg-form-status' );
212 - const $actions = $button.closest( '.ayg-form-actions' );
213 - const isNew = '0' === $form.find( 'input[name="gallery_id"]' ).val();
214 -
215 - // Validate the active source field, unless it is locked (disabled). The source
216 - // type maps to a single value field; livestream reuses the channel field.
217 - const type = $form.find( '#ayg-form-field-type' ).val();
218 - const sourceFieldMap = { playlist: 'playlist', channel: 'channel', livestream: 'channel', username: 'username', search: 'search', video: 'video', videos: 'videos' };
219 - const sourceField = sourceFieldMap[ type ];
220 - const $sourceField = $form.find( '#ayg-form-field-' + sourceField );
221 -
222 - if ( $sourceField.length && ! $sourceField.prop( 'disabled' ) ) {
223 - const $control = $sourceField.closest( '.ayg-form-control' );
224 - const value = $.trim( $sourceField.val() );
225 -
226 - // Show a field-level message, focus and scroll to the field, then stop
227 - const showSourceError = function( message ) {
228 - $control.addClass( 'ayg-form-invalid' );
229 -
230 - const $error = $( '<span class="ayg-text-error"><span class="dashicons dashicons-dismiss" aria-hidden="true"></span></span>' );
231 - $error.append( document.createTextNode( message ) );
232 - $control.find( '.ayg-form-status' ).html( '' ).append( $error );
233 -
234 - const field = $sourceField.get( 0 );
235 - field.focus( { preventScroll: true } );
236 - field.scrollIntoView( { behavior: 'smooth', block: 'center' } );
237 - };
238 -
239 - if ( ! value ) {
240 - showSourceError( ayg_admin.i18n.source_required[ type ] );
241 - return;
185 + // field type = checkbox
186 + if ( 'checkbox' == type ) {
187 + value = $elem.is( ':checked' ) ? 1 : 0;
242 188 }
243 189
244 - // @handle URLs can't be resolved for channel / livestream sources
245 - if ( ( 'channel' === type || 'livestream' === type ) && value.indexOf( '@' ) !== -1 ) {
246 - showSourceError( ayg_admin.i18n.channel_handle );
247 - return;
190 + // source = playlist
191 + if ( 'playlist' == key ) {
192 + value = ayg_get_youtube_playlist_id( value );
248 193 }
249 - }
250 194
251 - // Disable the buttons and show a live status (spinner + green message + animated dots).
252 - // Reuses the existing message element when present so the spinner and dots keep
253 - // animating across updates instead of restarting on each call.
254 - const showStatus = function( message ) {
255 - $actions.find( 'button' ).prop( 'disabled', true );
195 + // source = channel
196 + if ( 'channel' == key ) {
197 + var result = ayg_get_youtube_channel_id( value );
198 +
199 + key = result.type;
200 + value = result.id;
256 201
257 - let $message = $status.find( '.ayg-text-success' );
258 -
259 - if ( ! $message.length ) {
260 - $message = $( '<span class="ayg-text-success"></span>' ).append( '<span class="ayg-animate-dots"></span>' );
261 - $status.html( '<span class="spinner"></span>' ).append( $message );
202 + if ( props.hasOwnProperty( 'type' ) && 'channel' == props.type ) {
203 + props.type = key;
204 + }
262 205 }
263 206
264 - // Update only the leading text node, leaving the trailing dots span intact.
265 - const node = $message.get( 0 ).firstChild;
266 -
267 - if ( node && 3 === node.nodeType ) {
268 - node.nodeValue = message;
269 - } else {
270 - $message.prepend( document.createTextNode( message ) );
207 + // source = video
208 + if ( 'video' == key ) {
209 + value = ayg_get_youtube_video_id( value );
271 210 }
272 - };
273 211
274 - // Restore the buttons and show an error message in the form status
275 - const showError = function( message ) {
276 - $actions.find( 'button' ).prop( 'disabled', false );
212 + // source = videos
213 + if ( 'videos' == key ) {
214 + var lines = value.split( '\n' ),
215 + ids = [];
277 216
278 - const $error = $( '<span class="ayg-text-error"><span class="dashicons dashicons-dismiss" aria-hidden="true"></span></span>' );
279 - $error.append( document.createTextNode( message ) );
280 - $status.html( '' ).append( $error );
281 - };
282 -
283 - // Fill a status template's %imported% / %updated% / %deleted% tokens from an import response.
284 - const fillCounts = function( template, data ) {
285 - return template
286 - .replace( '%imported%', data && data.imported ? data.imported : 0 )
287 - .replace( '%updated%', data && data.updated ? data.updated : 0 )
288 - .replace( '%deleted%', data && data.deleted ? data.deleted : 0 );
289 - };
290 -
291 - // Stage 1: "Processing..."
292 - showStatus( ayg_admin.i18n.processing );
293 -
294 - // Perform AJAX request to save the gallery
295 - const data = $form.serialize() + '&action=ayg_save_gallery&security=' + encodeURIComponent( ayg_admin.ajax_nonce );
296 -
297 - $.post( ajaxurl, data, function( response ) {
298 - if ( ! response.success ) {
299 - showError( ( response.data && response.data.message ) ? response.data.message : ayg_admin.i18n.save_failed );
300 - return;
217 + lines.map(function( url ) {
218 + ids.push( ayg_get_youtube_video_id( url ) );
219 + });
220 +
221 + value = ids.join( ',' );
301 222 }
223 +
224 + // Add only if the user input differ from the global configuration
225 + if ( value != def ) {
226 + props[ key ] = value;
227 + }
228 + });
302 229
303 - form_dirty = false;
230 + // If popup is enabled and type is video, force theme to popup
231 + if ( popup && props.hasOwnProperty( 'type' ) && 'video' == props.type ) {
232 + props.theme = 'popup';
233 + }
304 234
305 - const id = response.data.gallery_id;
306 - const editUrl = ayg_admin.admin_url + '?page=automatic-youtube-gallery&action=edit&id=' + id;
307 - const doneUrl = editUrl + ( isNew ? '&saved=1' : '&updated=1' );
235 + // Build attributes string
236 + var attrs = '';
308 237
309 - // "Import videos" unchecked = display-only edit; save the config but skip the import.
310 - const $canImport = $form.find( '#ayg-can-import-videos' );
311 - if ( $canImport.length && ! $canImport.is( ':checked' ) ) {
312 - window.location.href = doneUrl;
313 - return;
238 + for ( var key in props ) {
239 + if ( props.hasOwnProperty( key ) ) {
240 + attrs += ( ' ' + key + '="' + props[ key ] + '"' );
314 241 }
315 -
316 - // Paused galleries skip importing — saving must not re-trigger the import,
317 - // which would flip import_status back from 'paused' to 'idle'.
318 - if ( 'paused' === $form.find( '[name="schedule"]' ).val() ) {
319 - window.location.href = doneUrl;
320 - return;
321 - }
322 -
323 - // Live galleries (search / livestream / single video) query the live API at display time — nothing to import
324 - const sourceType = $form.find( '#ayg-form-field-type' ).val();
325 - if ( 'search' === sourceType || 'livestream' === sourceType || 'video' === sourceType ) {
326 - window.location.href = doneUrl;
327 - return;
328 - }
329 -
330 - // Point the form at the saved gallery before importing, so a refresh or a
331 - // retry after an import error updates this gallery instead of creating a new one
332 - $form.find( 'input[name="gallery_id"]' ).val( id );
333 - window.history.replaceState( null, '', editUrl );
334 -
335 - // Block the leave-warning's "import in progress" guard for the duration of the run.
336 - importing = true;
337 -
338 - importGallery( id, {
339 - progress: function( data ) {
340 - // Stage 2: Running "Imported: X, Updated: Y..." breakdown as batches return.
341 - showStatus( fillCounts( ayg_admin.i18n.import_progress, data ) );
342 - },
343 - complete: function( data ) {
344 - // Stage 3: Final breakdown, then redirect.
345 - importing = false;
346 - showStatus( fillCounts( ayg_admin.i18n.import_complete, data ) );
347 - window.location.href = doneUrl;
348 - },
349 - error: function( data ) {
350 - importing = false;
351 - showError( data.message || ( data.quota_exceeded ? ayg_admin.i18n.quota_exceeded : ayg_admin.i18n.import_failed ) );
352 -
353 - // The gallery exists now — clicking the button again updates and resumes
354 - if ( isNew ) {
355 - $button.text( ayg_admin.i18n.update_gallery );
356 - }
357 - }
358 - } );
359 - } ).fail( function() {
360 - showError( ayg_admin.i18n.save_failed );
361 - } );
362 - } );
363 -
364 - // Gallery List: Confirm before bulk deleting galleries
365 - $( '#ayg-gallery-list' ).on( 'submit', '.ayg-form', function( event ) {
366 - const action = $( this ).find( 'select[name="action"], select[name="action2"]' ).filter( function() {
367 - return 'delete' === $( this ).val();
368 - } );
369 -
370 - if ( action.length && ! window.confirm( ayg_admin.i18n.confirm_bulk_delete ) ) {
371 - event.preventDefault();
372 242 }
373 - } );
374 243
375 - // Gallery List / Form: Delete Gallery
376 - $( document ).on( 'click', '.ayg-button-delete-gallery', function( event ) {
377 - event.preventDefault();
244 + // Shortcode output
245 + $( '#aiovg-shortcode').val( '[automatic_youtube_gallery' + attrs + ']' );
246 + });
378 247
379 - if ( ! window.confirm( ayg_admin.i18n.confirm_delete ) ) {
380 - return;
381 - }
248 + // Editor: Toggle between field sections
249 + $( document ).on( 'click', '.ayg-editor-section-header', function( e ) {
250 + var $elem = $( this ).parent();
382 251
383 - const $button = $( this );
384 - const id = $button.data( 'id' );
252 + if ( ! $elem.hasClass( 'ayg-active' ) ) {
253 + $( this ).closest( '.ayg-editor' )
254 + .find( '.ayg-editor-section.ayg-active' )
255 + .toggleClass( 'ayg-active' )
256 + .find( '.ayg-editor-controls' )
257 + .slideToggle();
258 + }
385 259
386 - // Disable the button while deleting
387 - $button.prop( 'disabled', true );
260 + $elem.toggleClass( 'ayg-active' )
261 + .find( '.ayg-editor-controls' )
262 + .slideToggle();
263 + });
388 264
389 - // Perform AJAX request to delete the gallery
390 - const data = {
391 - action: 'ayg_delete_gallery',
392 - id: id,
393 - security: ayg_admin.ajax_nonce
394 - };
395 -
396 - $.post( ajaxurl, data, function( response ) {
397 - if ( ! response.success ) {
398 - const message = ( response.data && response.data.message ) ? response.data.message : ayg_admin.i18n.delete_failed;
399 - window.alert( message );
400 - $button.prop( 'disabled', false );
401 - return;
402 - }
403 -
404 - form_dirty = false;
405 -
406 - const doneUrl = ayg_admin.admin_url + '?page=automatic-youtube-gallery&deleted=1';
407 - window.location.href = doneUrl;
408 - } ).fail( function() {
409 - window.alert( ayg_admin.i18n.delete_failed );
410 - $button.prop( 'disabled', false );
411 - } );
412 - } );
413 -
414 - // Gallery List / Form: Copy Shortcode button
415 - $( document ).on( 'click', '.ayg-button-copy-shortcode', function() {
416 - const text = $( this ).data( 'clipboard-text' );
417 -
418 - // Confirm the copy with an alert that echoes the copied shortcode.
419 - const showCopied = function() {
420 - window.alert( ayg_admin.i18n.shortcode_copied + '\n\n' + text );
421 - }
422 -
423 - // Use the modern Clipboard API if available, otherwise fall back to the legacy method
424 - if ( navigator.clipboard && navigator.clipboard.writeText ) {
425 - navigator.clipboard.writeText( text ).then( showCopied );
426 - } else {
427 - const $temp = $( '<textarea>' ).css( { position: 'fixed', opacity: 0 } ).val( text ).appendTo( 'body' );
428 - $temp.get( 0 ).select();
429 - document.execCommand( 'copy' );
430 - $temp.remove();
431 -
432 - showCopied();
433 - }
434 - } );
435 -
436 265 // Editor: Toggle fields based on the source type
437 - $( document ).on( 'change', '.ayg-form-field-type', function() {
438 - const $wrapper = $( this ).closest( '.ayg-form' );
439 - const value = $( this ).val();
266 + $( document ).on( 'change', '.ayg-editor-field-type', function() {
267 + var $container = $( this ).closest( '.ayg-editor' );
268 + var value = $( this ).val();
440 269
441 - $wrapper.removeClass(function( index, classes ) {
442 - const matches = classes.match( /\ayg-form-field-type-\S+/ig );
270 + $container.removeClass(function( index, classes ) {
271 + var matches = classes.match( /\ayg-editor-field-type-\S+/ig );
443 272 return ( matches ) ? matches.join( ' ' ) : '';
444 273 });
445 274
446 - $wrapper.addClass( 'ayg-form-field-type-' + value );
447 -
448 - // Gallery Form: keep the source hint in sync with the selected type (live vs importable).
449 - // No-op where the hint isn't present (e.g. the widget form), so it stays safe globally.
450 - const $hint = $wrapper.find( '.ayg-source-hint' );
451 - if ( $hint.length ) {
452 - $hint.text( ayg_admin.live_types.indexOf( value ) !== -1 ? ayg_admin.i18n.source_live : ayg_admin.i18n.source_importable );
453 - }
275 + $container.addClass( 'ayg-editor-field-type-' + value );
454 276 });
455 277
456 278 // Editor: Toggle fields based on the theme
457 - $( document ).on( 'change', '.ayg-form-field-theme', function() {
458 - const $wrapper = $( this ).closest( '.ayg-form' );
459 - const value = $( this ).val();
279 + $( document ).on( 'change', '.ayg-editor-field-theme', function() {
280 + var $container = $( this ).closest( '.ayg-editor' );
281 + var value = $( this ).val();
460 282
461 - $wrapper.removeClass(function( index, classes ) {
462 - const matches = classes.match( /\ayg-form-field-theme-\S+/ig );
283 + $container.removeClass(function( index, classes ) {
284 + var matches = classes.match( /\ayg-editor-field-theme-\S+/ig );
463 285 return ( matches ) ? matches.join( ' ' ) : '';
464 286 });
465 287
466 - $wrapper.addClass( 'ayg-form-field-theme-' + value );
288 + $container.addClass( 'ayg-editor-field-theme-' + value );
467 289 });
468 290
469 291 // Settings: Toggle fields based on the theme
470 - $( '#ayg-settings' ).on( 'change', 'tr.theme select', function() {
471 - const $wrapper = $( '#ayg-settings' );
472 - const value = $( this ).val();
292 + $( 'tr.theme select', '#ayg-settings' ).on( 'change', function() {
293 + var $container = $( '#ayg-settings' );
294 + var value = $( this ).val();
473 295
474 - $wrapper.removeClass(function( index, classes ) {
475 - const matches = classes.match( /theme-\S+/ig );
296 + $container.removeClass(function( index, classes ) {
297 + var matches = classes.match( /theme-\S+/ig );
476 298 return ( matches ) ? matches.join( ' ' ) : '';
477 299 });
478 300
479 - $wrapper.addClass( 'theme-' + value );
301 + $container.addClass( 'theme-' + value );
480 302 });
481 303
482 304 // Settings: Toggle fields based on the player type
483 - $( '#ayg-settings' ).on( 'change', 'tr.player_type input[type="radio"]', function() {
484 - const $wrapper = $( '#ayg-settings' );
485 - const value = $wrapper.find( 'tr.player_type input[type="radio"]:checked' ).val();
305 + $( 'tr.player_type input[type="radio"]', '#ayg-settings' ).on( 'change', function() {
306 + var $container = $( '#ayg-settings' );
307 + var value = $container.find( 'tr.player_type input[type="radio"]:checked' ).val();
486 308
487 - $wrapper.removeClass(function( index, classes ) {
488 - const matches = classes.match( /player_type-\S+/ig );
309 + $container.removeClass(function( index, classes ) {
310 + var matches = classes.match( /player_type-\S+/ig );
489 311 return ( matches ) ? matches.join( ' ' ) : '';
490 312 });
491 313
492 - $wrapper.addClass( 'player_type-' + value );
314 + $container.addClass( 'player_type-' + value );
493 315 });
494 316
495 - // Settings: Upload button
496 - $( '.ayg-button-upload-media' ).on( 'click', function( event ) {
497 - event.preventDefault();
498 - renderMediaUploader( $( this ) );
317 + // Settings: Browse button
318 + $( '.ayg-settings-browse' ).on( 'click', function( e ) {
319 + e.preventDefault();
320 + ayg_render_media_uploader( $( this ) );
499 321 });
500 322
501 323 // Settings: Delete cache
502 - $( '#ayg-button-delete-cache' ).on( 'click', function( event ) {
503 - event.preventDefault();
324 + $( '#ayg-button-delete-cache' ).on( 'click', function( e ) {
325 + e.preventDefault();
326 +
327 + $( this ).prop( 'disabled', true );
328 + $( '.ayg-ajax-status', '#ayg-table-delete-cache' ).html( '<span class="spinner"></span>' );
504 329
505 - const $button = $( this );
506 -
507 - // Disable the button and show a spinner while deleting cache
508 - $button.prop( 'disabled', true );
509 - $( '.ayg-form-status', '#ayg-table-delete-cache' ).html( '<span class="spinner"></span>' );
510 -
511 - // Perform AJAX request to delete the cache
512 - const data = {
330 + var data = {
513 331 'action': 'ayg_delete_cache',
514 332 'security': ayg_admin.ajax_nonce
515 333 };
516 -
334 +
517 335 $.post( ajaxurl, data, function( response ) {
518 - if ( ! response.success ) {
519 - const message = ( response.data && response.data.message ) ? response.data.message : ayg_admin.i18n.delete_failed;
520 -
521 - $button.prop( 'disabled', false );
522 - $( '.ayg-form-status', '#ayg-table-delete-cache' ).html( '<span class="ayg-text-error"><span class="dashicons dashicons-dismiss" aria-hidden="true"></span>' + message + '</span>' );
523 - return;
524 - }
525 -
526 - $button.prop( 'disabled', false );
527 - $( '.ayg-form-status', '#ayg-table-delete-cache' ).html( '<span class="ayg-text-success"><span class="dashicons dashicons-yes-alt" aria-hidden="true"></span>' + ayg_admin.i18n.cache_cleared + '</span>' );
528 - } ).fail( function() {
529 - $button.prop( 'disabled', false );
530 - $( '.ayg-form-status', '#ayg-table-delete-cache' ).html( '<span class="ayg-text-error"><span class="dashicons dashicons-dismiss" aria-hidden="true"></span>' + ayg_admin.i18n.delete_failed + '</span>' );
531 - } );
336 + $( this ).prop( 'disabled', false );
337 + $( '.ayg-ajax-status', '#ayg-table-delete-cache' ).html( '<span class="ayg-text-success">' + ayg_admin.i18n.cleared + '</span>' );
338 + });
532 339 });
533 340
534 341 // Widget: Initiate color picker
535 342 $( '#widgets-right .widget:has(.ayg-color-picker)' ).each(function() {
536 - initWidgetColorPicker( $( this ) );
343 + ayg_widget_color_picker( $( this ) );
537 344 });
538 345
539 - $( document ).on( 'widget-added widget-updated', onWidgetUpdate );
346 + $( document ).on( 'widget-added widget-updated', on_ayg_widget_update );
540 347 });
541 348
542 349 })( jQuery );