Plugin Directory

Changeset 3305514


Ignore:
Timestamp:
06/03/2025 07:33:15 AM (9 months ago)
Author:
webzunft
Message:

version 3.3.0

Location:
image-source-control-isc
Files:
18 added
2 deleted
22 edited
26 copied

Legend:

Unmodified
Added
Removed
  • image-source-control-isc/tags/3.3.0/admin/templates/settings/preview/caption-preview.html

    r3217506 r3305514  
    1010        // Create the isc_front_data object.
    1111        var isc_front_data = {
    12             caption_position: caption_position
     12            caption_position: caption_position,
     13            caption_style: {
     14                'position': 'absolute',
     15                'font-size': '0.9em',
     16                'background-color': '#333',
     17                'color': '#fff',
     18                'opacity': '0.70',
     19                'padding': '0 0.15em',
     20                'text-shadow': 'none',
     21                'display': 'block'
     22            }
    1323        };
    1424
  • image-source-control-isc/tags/3.3.0/includes/block-options/block-options.php

    r3256989 r3305514  
    22
    33use ISC\Plugin;
     4use ISC\Helpers;
    45
    56/**
     
    177178        $dependencies = [ 'jquery', 'wp-api', 'lodash', 'wp-blocks', 'wp-element', 'wp-i18n' ];
    178179        $screen       = get_current_screen();
    179         wp_enqueue_script( 'isc_attachment_compat', trailingslashit( ISCBASEURL ) . 'admin/assets/js/wp.media.view.AttachmentCompat.js', [ 'media-upload' ], ISCVERSION, true );
     180        Helpers::enqueue_script( 'isc_attachment_compat', 'admin/assets/js/wp.media.view.AttachmentCompat.js', [ 'media-upload' ] );
    180181
    181182        if ( $screen && isset( $screen->base ) && $screen->base !== 'widgets' ) {
    182183            $dependencies[] = 'wp-editor';
    183184        }
    184         wp_register_script(
     185        Helpers::register_script(
    185186            'isc/image-block',
    186             plugin_dir_url( __FILE__ ) . 'isc-image-block.js',
     187            'includes/block-options/isc-image-block.js',
    187188            $dependencies,
    188             true,
    189             false
    190189        );
    191190
     
    204203            wp_add_inline_script( 'isc/image-block', 'var iscData = ' . wp_json_encode( $isc_data ) . ';', 'before' );
    205204        }
     205        // separated from register_script since wp_add_inline_script needs a working handle
    206206        wp_enqueue_script( 'isc/image-block' );
    207207        wp_set_script_translations( 'isc/image-block', 'image-source-control-isc', apply_filters( 'isc_path_to_languages', '' ) );
    208208
    209         wp_enqueue_script( 'isc/media-upload', trailingslashit( ISCBASEURL ) . 'admin/assets/js/media-upload.js', [ 'media-upload' ], ISCVERSION, true );
     209        Helpers::enqueue_script( 'isc/media-upload', 'admin/assets/js/media-upload.js', [ 'media-upload' ] );
    210210    }
    211211
     
    214214     */
    215215    public function edit_link_assets() {
    216         wp_enqueue_script(
    217             'isc/image-block-edit-link',
    218             plugin_dir_url( __FILE__ ) . 'isc-image-block-edit-link.js',
    219             [ 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor' ],
    220             ISCVERSION,
    221             true // load in the footer
    222         );
     216        Helpers::enqueue_script( 'isc/image-block-edit-link', 'includes/block-options/isc-image-block-edit-link.js', [ 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor' ] );
    223217
    224218        wp_set_script_translations( 'isc/image-block-edit-link', 'image-source-control-isc', apply_filters( 'isc_path_to_languages', '' ) );
  • image-source-control-isc/tags/3.3.0/includes/feedback.php

    r3253182 r3305514  
    4343        }
    4444
    45         wp_enqueue_script( 'isc-feedback', ISCBASEURL . 'admin/assets/js/feedback.js', [], ISCVERSION, true );
     45        Helpers::enqueue_script( 'isc-feedback', 'admin/assets/js/feedback.js' );
    4646        wp_enqueue_style( 'isc-feedback', ISCBASEURL . 'admin/assets/css/feedback.css', [], ISCVERSION );
    4747    }
  • image-source-control-isc/tags/3.3.0/includes/helpers.php

    r3037704 r3305514  
    4545        return $data;
    4646    }
     47
     48    /**
     49     * Wrapper for wp_enqueue_script() to handle various script dependencies
     50     *
     51     * @param string $handle     The script handle.
     52     * @param string $src        The relative script source path. The base URL will be added dynamically.
     53     * @param array  $deps       An array of script handles this script depends on.
     54     * @param bool   $in_footer   Whether to enqueue the script in the footer.
     55     *
     56     * @return void
     57     */
     58    public static function enqueue_script( string $handle, string $src, array $deps = [], bool $in_footer = true ) {
     59        wp_enqueue_script( $handle, self::get_script_url( $src ), $deps, ISCVERSION, $in_footer );
     60    }
     61
     62    /**
     63     * Wrapper for wp_register_script() to handle various script dependencies
     64     *
     65     * @param string $handle     The script handle.
     66     * @param string $src        The relative script source path. The base URL will be added dynamically.
     67     * @param array  $deps       An array of script handles this script depends on.
     68     * @param bool   $in_footer   Whether to enqueue the script in the footer.
     69     *
     70     * @return void
     71     */
     72    public static function register_script( string $handle, string $src, array $deps = [], bool $in_footer = true ) {
     73        wp_register_script( $handle, self::get_script_url( $src ), $deps, ISCVERSION, $in_footer );
     74    }
     75
     76    /**
     77     * Get script URL
     78     *
     79     * @param string $src The relative script source path.
     80     *
     81     * @return string The full URL to the script, with the base URL prepended and minified if SCRIPT_DEBUG is off.
     82     */
     83    public static function get_script_url( string $src ): string {
     84        // load the minified script version if SCRIPT_DEBUG is off
     85        if ( ! SCRIPT_DEBUG && strpos( $src, '.min.js' ) === false ) {
     86            $src = str_replace( '.js', '.min.js', $src );
     87        }
     88
     89        // fix slashes
     90        return trailingslashit( ISCBASEURL ) . ltrim( $src, '/' );
     91    }
    4792}
  • image-source-control-isc/tags/3.3.0/includes/image-sources/admin/scripts.php

    r3253182 r3305514  
    44
    55use ISC\Admin_Utils;
     6use ISC\Helpers;
    67
    78/**
     
    2829
    2930        if ( $screen->id === 'media_page_isc-sources' ) {
    30             wp_enqueue_script( 'isc_sources_script', ISCBASEURL . '/admin/assets/js/sources.js', [], ISCVERSION, true );
     31            Helpers::enqueue_script( 'isc_sources_script', 'admin/assets/js/sources.js' );
    3132        }
    3233
     
    3738
    3839        if ( in_array( $screen->id, [ 'upload', 'widgets', 'customize' ], true ) ) {
    39             wp_enqueue_script( 'isc_attachment_compat', ISCBASEURL . '/admin/assets/js/wp.media.view.AttachmentCompat.js', [ 'media-upload' ], ISCVERSION, true );
     40            Helpers::enqueue_script( 'isc_attachment_compat', 'admin/assets/js/wp.media.view.AttachmentCompat.js', [ 'media-upload' ] );
    4041        }
    4142    }
  • image-source-control-isc/tags/3.3.0/includes/settings.php

    r3253281 r3305514  
    7070
    7171        if ( isset( $screen->id ) && $screen->id === 'settings_page_isc-settings' ) {
    72             wp_enqueue_script( 'isc_settings_script', ISCBASEURL . '/admin/assets/js/settings.js', [], ISCVERSION, true );
     72            Helpers::enqueue_script( 'isc_settings_script', 'admin/assets/js/settings.js' );
    7373        }
    7474    }
  • image-source-control-isc/tags/3.3.0/includes/settings/sections/page-list.php

    r3253182 r3305514  
    1717        add_settings_field( 'source_type_list', __( 'Enable', 'image-source-control-isc' ), [ $this, 'render_field_source_type_list' ], 'isc_settings_page', 'isc_settings_section_list_below_content' );
    1818        add_settings_field( 'image_list_headline', __( 'Headline', 'image-source-control-isc' ), [ $this, 'render_field_list_headline' ], 'isc_settings_page', 'isc_settings_section_list_below_content' );
     19        add_settings_field( 'list_layout', __( 'Layout', 'image-source-control-isc' ), [ $this, 'render_field_list_layout' ], 'isc_settings_page', 'isc_settings_section_list_below_content' );
    1920        add_settings_field( 'below_content_included_images', __( 'Included images', 'image-source-control-isc' ), [ $this, 'render_field_below_content_included_images' ], 'isc_settings_page', 'isc_settings_section_list_below_content' );
    2021    }
     
    4142        $options = $this->get_options();
    4243        require_once ISCPATH . '/admin/templates/settings/page-list/headline.php';
     44    }
     45
     46    /**
     47     * Render option to define the layout of the image list
     48     */
     49    public function render_field_list_layout() {
     50        $options             = $this->get_options();
     51        $list_layout_details = ! empty( ( $options['list_layout'] ?? [] )['details'] ?? false );
     52        $is_pro_enabled      = \ISC\Plugin::is_pro();
     53        require_once ISCPATH . '/admin/templates/settings/page-list/layout.php';
    4354    }
    4455
  • image-source-control-isc/tags/3.3.0/isc.php

    r3283881 r3305514  
    22/**
    33 * Plugin Name: Image Source Control Lite
    4  * Version: 3.2.0
     4 * Version: 3.3.0
    55 * Plugin URI: https://imagesourcecontrol.com/
    66 * Description: Image Source Control saves the source of an image, lists them and warns if it is missing.
     
    3030}
    3131
    32 define( 'ISCVERSION', '3.2.0' );
     32define( 'ISCVERSION', '3.3.0' );
    3333define( 'ISCNAME', 'Image Source Control' );
    3434define( 'ISCDIR', basename( __DIR__ ) );
  • image-source-control-isc/tags/3.3.0/lib/composer/autoload_classmap.php

    r3283881 r3305514  
    3737    'ISC\\Pro\\Custom_Attribute_Processor' => $baseDir . '/pro/includes/Custom_Attribute_Processor.php',
    3838    'ISC\\Pro\\IPTC' => $baseDir . '/pro/includes/IPTC.php',
     39    'ISC\\Pro\\List_Layout_Details' => $baseDir . '/pro/includes/List_Layout_Details.php',
    3940    'ISC\\Settings' => $baseDir . '/includes/settings.php',
    4041    'ISC\\Settings\\Section' => $baseDir . '/includes/settings/section.php',
  • image-source-control-isc/tags/3.3.0/lib/composer/autoload_static.php

    r3283881 r3305514  
    5757        'ISC\\Pro\\Custom_Attribute_Processor' => __DIR__ . '/../..' . '/pro/includes/Custom_Attribute_Processor.php',
    5858        'ISC\\Pro\\IPTC' => __DIR__ . '/../..' . '/pro/includes/IPTC.php',
     59        'ISC\\Pro\\List_Layout_Details' => __DIR__ . '/../..' . '/pro/includes/List_Layout_Details.php',
    5960        'ISC\\Settings' => __DIR__ . '/../..' . '/includes/settings.php',
    6061        'ISC\\Settings\\Section' => __DIR__ . '/../..' . '/includes/settings/section.php',
  • image-source-control-isc/tags/3.3.0/public/assets/js/captions.js

    r3217506 r3305514  
    77
    88// Use values from isc_front_data if available, otherwise use default values
    9 const ISC_FONT_SIZE                 = isc_front_data.font_size || '0.9em';
    10 const ISC_BACKGROUND_COLOR          = isc_front_data.background_color || '#333';
    11 const ISC_TEXT_COLOR                = isc_front_data.text_color || '#fff';
    12 const ISC_OPACITY                   = isc_front_data.opacity || 0.70;
    13 const ISC_PADDING                   = isc_front_data.padding || '0 0.15em';
    14 const ISC_DISPLAY                   = isc_front_data.display || 'block';
    159const ISC_Z_INDEX                   = isc_front_data.z_index || '9999';
    1610const ISC_CAPTION_HORIZONTAL_MARGIN = isc_front_data.caption_horizontal_margin || 5;
    1711const ISC_CAPTION_VERTICAL_MARGIN   = isc_front_data.caption_vertical_margin || 5;
     12const ISC_STYLE_STRING = (() => {
     13    const styles = isc_front_data.caption_style || {};
     14    let styleString = '';
     15
     16    for ( const [property, value] of Object.entries(styles) ) {
     17        styleString += `${property}: ${value}; `;
     18    }
     19
     20    return styleString.trim();
     21})();
    1822
    1923/**
     
    2832            const l        = captions.length;
    2933            for ( let i = 0; i < l; i++ ) {
    30                 captions[i].setAttribute( "style", `position: absolute; font-size: ${ISC_FONT_SIZE}; background-color: ${ISC_BACKGROUND_COLOR}; color: ${ISC_TEXT_COLOR}; opacity: ${ISC_OPACITY}; padding: ${ISC_PADDING}; text-shadow: none; display: ${ISC_DISPLAY}` );
    31                 // Some themes handle the bottom padding of the attachment's div with the caption text (which is in between
    32                 // the image and the bottom border) not with the div itself. The following line set the padding on the bottom equal to the top.
    33                 captions[i].style.paddingBottom = window.getComputedStyle( captions[i] )['padding-top'];
     34                isc_update_caption_style( captions[i] );
    3435                // position the parent element (.isc-source)
    3536                isc_update_caption_position( captions[i].parentNode );
     
    5354            isc_update_captions_positions();
    5455        } );
     56
     57        /**
     58         * Monitor DOM changes to update captions
     59         */
     60        isc_setup_mutation_observer();
    5561};
     62
     63/**
     64 * Iterate through all image source captions and set basic CSS style
     65 *
     66 * This can be used to add styles to dynamically added captions, e.g., by AJAX calls
     67 */
     68function isc_update_captions_styles() {
     69    const captions = document.querySelectorAll( '.isc-source .isc-source-text' );
     70    const l        = captions.length;
     71    for ( let i = 0; i < l; i++ ) {
     72        isc_update_caption_style( captions[i] );
     73    }
     74}
     75
     76/**
     77 * Add basic CSS to a single image source caption
     78 *
     79 * @param caption image source caption that needs positioning
     80 */
     81function isc_update_caption_style( caption ) {
     82    // bail if the element already has a style attribute
     83    if ( caption.hasAttribute( 'style' ) ) {
     84        return;
     85    }
     86
     87    caption.setAttribute( "style", ISC_STYLE_STRING );
     88    // Some themes handle the bottom padding of the attachment's div with the caption text (which is in between
     89    // the image and the bottom border) not with the div itself. The following line set the padding on the bottom equal to the top.
     90    caption.style.paddingBottom = window.getComputedStyle( caption )['padding-top'];
     91}
    5692
    5793/**
     
    180216    return el.offsetHeight + parseInt( style.marginTop ) + parseInt( style.marginBottom );
    181217}
     218
     219/**
     220 * Sets up the MutationObserver to watch for dynamically added captions.
     221 */
     222function isc_setup_mutation_observer() {
     223    // Get selectors for elements to observe from PHP, default to an empty array
     224    const elementsToObserveSelectors = isc_front_data.observe_elements_selectors || [];
     225
     226    if ( elementsToObserveSelectors.length === 0 ) {
     227        return; // Do nothing if no selectors are provided
     228    }
     229
     230    const observer = new MutationObserver( ( mutationsList, observerInstance) => {
     231        let newCaptionsDetected = false;
     232        for ( const mutation of mutationsList ) {
     233            // We are interested in mutations where nodes were added.
     234            if ( mutation.type === 'childList' && mutation.addedNodes.length > 0 ) {
     235                // Iterate through each node that was added.
     236                // An AJAX response might add multiple sibling elements at once.
     237                for ( const addedNode of mutation.addedNodes ) {
     238                    // We only operate on actual HTML elements.
     239                    if ( addedNode.nodeType === Node.ELEMENT_NODE ) {
     240                        // Check for the presence of the caption text
     241                        if ( addedNode.querySelector( '.isc-source .isc-source-text' ) ) {
     242                            newCaptionsDetected = true;
     243                            // Found what we're looking for, no need to check further
     244                            // added nodes in this particular mutation.
     245                            break;
     246                        }
     247                    }
     248                }
     249            }
     250            // If we've detected new captions in any of the mutations,
     251            // we can stop checking the rest of the mutations.
     252            if ( newCaptionsDetected ) {
     253                break;
     254            }
     255        }
     256
     257        if ( newCaptionsDetected ) {
     258            console.log('ISC MutationObserver: New captions detected. Updating styles and positions.');
     259            isc_update_captions_styles();
     260            isc_update_captions_positions();
     261        }
     262    });
     263
     264    elementsToObserveSelectors.forEach( selector => {
     265        const targetNode = document.querySelector(selector);
     266        if ( targetNode ) {
     267            observer.observe( targetNode, { childList: true, subtree: true } );
     268        }
     269    } );
     270}
  • image-source-control-isc/tags/3.3.0/public/public.php

    r3283881 r3305514  
    22
    33use ISC\Standard_Source;
     4use ISC\Helpers;
    45
    56/**
     
    128129        }
    129130        // inject in footer as we can only reliably position captions when the DOM is fully loaded
    130         wp_enqueue_script( 'isc_caption', plugins_url( '/assets/js/captions.js', __FILE__ ), null, ISCVERSION, true );
    131     }
    132 
     131        Helpers::enqueue_script( 'isc_caption', 'public/assets/js/captions.js' );
     132
     133        $options = $this->get_options();
     134
     135        $caption_style = [
     136            'position'         => 'absolute',
     137            'font-size'        => '0.9em',
     138            'background-color' => '#333',
     139            'color'            => '#fff',
     140            'opacity'          => '0.70',
     141            'padding'          => '0 0.15em',
     142            'text-shadow'      => 'none',
     143            'display'          => 'block',
     144        ];
     145
     146        $front_data = [
     147            'caption_position' => isset( $options['caption_position'] ) ? esc_html( $options['caption_position'] ) : '',
    133148            /**
    134              * Front-end scripts in <head /> section.
     149             * Filter: isc_public_caption_default_style
     150             * Allows to change the default caption style.
     151             *
     152             * @param array $caption_style The default caption style.
     153             * @param array $options The options array.
    135154             */
     155            'caption_style'    => apply_filters( 'isc_public_caption_default_style', $caption_style, $options ),
     156        ];
     157
     158        /**
     159         * Filter: isc_public_caption_script_options
     160         *
     161         * @param array $front_data The data to be localized.
     162         * @param array $options The options array.
     163         */
     164        $filtered_front_data = apply_filters( 'isc_public_caption_script_options', $front_data, $options );
     165
     166        wp_localize_script(
     167            'isc_caption',
     168            'isc_front_data',
     169            $filtered_front_data
     170        );
     171    }
     172
     173    /**
     174     * Front-end scripts in <head /> section.
     175     */
    136176    public function front_head() {
    137177        // don’t add the script on AMP pages
     
    142182        $options = $this->get_options();
    143183        ?>
    144             <script>
    145             /* <![CDATA[ */
    146                 var isc_front_data =
    147                 {
    148                     caption_position : '<?php echo esc_html( $options['caption_position'] ); ?>',
    149                 }
    150             /* ]]> */
    151             </script>
    152184            <style>
    153185                .isc-source { position: relative; display: inline-block; line-height: initial; }
     
    513545        }
    514546
    515         $options  = $this->get_options();
    516         $headline = $options['image_list_headline'];
    517 
    518547        ob_start();
    519548
    520549        ?>
    521             <p class="isc_image_list_title"><?php echo esc_html( $headline ); ?></p>
    522550            <ul class="isc_image_list">
    523551        <?php
     
    947975     *
    948976     * @param string $content content of the source box, i.e., list of sources.
    949      */
    950     public function render_image_source_box( $content = null ) {
     977     * @param bool   $create_placeholder if true, create a placeholder box without content.
     978     */
     979    public function render_image_source_box( string $content = '', bool $create_placeholder = false ): string {
     980        $options  = $this->get_options();
     981        $headline = $options['image_list_headline'];
     982
    951983        ob_start();
    952984        require ISCPATH . 'public/views/image-source-box.php';
     
    954986        ISC_Log::log( 'finished creating image source box' );
    955987
    956         return ob_get_clean();
     988        /**
     989         * Filter: isc_render_image_source_box
     990         * allow to modify the output of the image source box
     991         *
     992         * @param string $content content of the source box.
     993         * @param string $headline headline of the source box.
     994         * @param bool   $create_placeholder if true, create a placeholder box without content.
     995         */
     996        return apply_filters( 'isc_render_image_source_box', ob_get_clean(), $content, $headline, $create_placeholder );
    957997    }
    958998
  • image-source-control-isc/tags/3.3.0/public/views/image-source-box.php

    r2414060 r3305514  
    66 *
    77 * @var string $content list of image source or other content.
     8 * @var string $headline headline for the image list.
     9 * @var bool   $create_placeholder whether to create a placeholder or not.
    810 */
     11
    912?>
    10 <div class="isc_image_list_box"><?php echo isset( $content ) ? $content : ''; ?></div>
     13<div class="isc_image_list_box"><?php if ( ! $create_placeholder ) : ?>
     14    <p class="isc_image_list_title"><?php echo esc_html( $headline ); ?></p>
     15    <?php echo $content; ?>
     16<?php endif; ?></div>
  • image-source-control-isc/tags/3.3.0/readme.txt

    r3289894 r3305514  
    44Requires at least: 6.0
    55Tested up to: 6.8
    6 Stable tag: 3.2.0
     6Stable tag: 3.3.0
    77Requires PHP: 7.4
    88License: GPLv3 or later
     
    8989
    9090Extended compatibility with Elementor, Avada, WP Bakery, and other page builders
    91 as well as with plugins like WPML, Kadence Blocks, Kadence Related Content Carousel, and Lightbox Gallery.
     91as well as with plugins like WPML, Kadence Blocks, Kadence Related Content Carousel, Lightbox Gallery, and JetEngine.
    9292
    9393[See Pricing](https://imagesourcecontrol.com/pricing/?utm_source=wporg&utm_medium=link&utm_campaign=pricing).
     
    164164== Changelog ==
    165165
     166= 3.3.0 =
     167
     168- Feature (Pro): Layout option to collapse the Per-page list below the content by default and only open it on click
     169- Improvement: Minified JavaScript files in frontend and backend for faster load times
     170- Improvement (Pro): Consider the site_icon image as "used" and don’t list it under Unused Images
     171- Improvement (Pro): Support for JetEngine AJAX-loaded content
     172- Improvement (Pro): The expand-on-click caption option is more stable and should not create random line breaks
     173- Fix (Pro): Honor the Elementor compatibility option and disable the feature, if the option is not checked
     174- Fix (Pro): Made a string on the Indexer page translatable
     175- Dev: The main caption CSS can be customized via the `isc_public_caption_default_style` filter
     176- Dev: The new `isc_update_captions_styles()` JavaScript function allows developers to update the caption styles in the frontend. E.g., useful for dynamically loaded content
     177- Dev (Pro): Improved handling nested output buffer calls to prevent issues with other plugins; here it was WPBakery
     178- Dev (Pro): Use the `isc_unused_images_ids_considered_used` filter to add IDs of images that should not show as unused images
     179
    166180= 3.2.0 =
     181
     182April 29th, 2025
    167183
    168184- Feature: You can use the option “Images only” to disable features for non-images in the media library, e.g., PDF files
  • image-source-control-isc/trunk/admin/templates/settings/preview/caption-preview.html

    r3217506 r3305514  
    1010        // Create the isc_front_data object.
    1111        var isc_front_data = {
    12             caption_position: caption_position
     12            caption_position: caption_position,
     13            caption_style: {
     14                'position': 'absolute',
     15                'font-size': '0.9em',
     16                'background-color': '#333',
     17                'color': '#fff',
     18                'opacity': '0.70',
     19                'padding': '0 0.15em',
     20                'text-shadow': 'none',
     21                'display': 'block'
     22            }
    1323        };
    1424
  • image-source-control-isc/trunk/includes/block-options/block-options.php

    r3256989 r3305514  
    22
    33use ISC\Plugin;
     4use ISC\Helpers;
    45
    56/**
     
    177178        $dependencies = [ 'jquery', 'wp-api', 'lodash', 'wp-blocks', 'wp-element', 'wp-i18n' ];
    178179        $screen       = get_current_screen();
    179         wp_enqueue_script( 'isc_attachment_compat', trailingslashit( ISCBASEURL ) . 'admin/assets/js/wp.media.view.AttachmentCompat.js', [ 'media-upload' ], ISCVERSION, true );
     180        Helpers::enqueue_script( 'isc_attachment_compat', 'admin/assets/js/wp.media.view.AttachmentCompat.js', [ 'media-upload' ] );
    180181
    181182        if ( $screen && isset( $screen->base ) && $screen->base !== 'widgets' ) {
    182183            $dependencies[] = 'wp-editor';
    183184        }
    184         wp_register_script(
     185        Helpers::register_script(
    185186            'isc/image-block',
    186             plugin_dir_url( __FILE__ ) . 'isc-image-block.js',
     187            'includes/block-options/isc-image-block.js',
    187188            $dependencies,
    188             true,
    189             false
    190189        );
    191190
     
    204203            wp_add_inline_script( 'isc/image-block', 'var iscData = ' . wp_json_encode( $isc_data ) . ';', 'before' );
    205204        }
     205        // separated from register_script since wp_add_inline_script needs a working handle
    206206        wp_enqueue_script( 'isc/image-block' );
    207207        wp_set_script_translations( 'isc/image-block', 'image-source-control-isc', apply_filters( 'isc_path_to_languages', '' ) );
    208208
    209         wp_enqueue_script( 'isc/media-upload', trailingslashit( ISCBASEURL ) . 'admin/assets/js/media-upload.js', [ 'media-upload' ], ISCVERSION, true );
     209        Helpers::enqueue_script( 'isc/media-upload', 'admin/assets/js/media-upload.js', [ 'media-upload' ] );
    210210    }
    211211
     
    214214     */
    215215    public function edit_link_assets() {
    216         wp_enqueue_script(
    217             'isc/image-block-edit-link',
    218             plugin_dir_url( __FILE__ ) . 'isc-image-block-edit-link.js',
    219             [ 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor' ],
    220             ISCVERSION,
    221             true // load in the footer
    222         );
     216        Helpers::enqueue_script( 'isc/image-block-edit-link', 'includes/block-options/isc-image-block-edit-link.js', [ 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor' ] );
    223217
    224218        wp_set_script_translations( 'isc/image-block-edit-link', 'image-source-control-isc', apply_filters( 'isc_path_to_languages', '' ) );
  • image-source-control-isc/trunk/includes/feedback.php

    r3253182 r3305514  
    4343        }
    4444
    45         wp_enqueue_script( 'isc-feedback', ISCBASEURL . 'admin/assets/js/feedback.js', [], ISCVERSION, true );
     45        Helpers::enqueue_script( 'isc-feedback', 'admin/assets/js/feedback.js' );
    4646        wp_enqueue_style( 'isc-feedback', ISCBASEURL . 'admin/assets/css/feedback.css', [], ISCVERSION );
    4747    }
  • image-source-control-isc/trunk/includes/helpers.php

    r3037704 r3305514  
    4545        return $data;
    4646    }
     47
     48    /**
     49     * Wrapper for wp_enqueue_script() to handle various script dependencies
     50     *
     51     * @param string $handle     The script handle.
     52     * @param string $src        The relative script source path. The base URL will be added dynamically.
     53     * @param array  $deps       An array of script handles this script depends on.
     54     * @param bool   $in_footer   Whether to enqueue the script in the footer.
     55     *
     56     * @return void
     57     */
     58    public static function enqueue_script( string $handle, string $src, array $deps = [], bool $in_footer = true ) {
     59        wp_enqueue_script( $handle, self::get_script_url( $src ), $deps, ISCVERSION, $in_footer );
     60    }
     61
     62    /**
     63     * Wrapper for wp_register_script() to handle various script dependencies
     64     *
     65     * @param string $handle     The script handle.
     66     * @param string $src        The relative script source path. The base URL will be added dynamically.
     67     * @param array  $deps       An array of script handles this script depends on.
     68     * @param bool   $in_footer   Whether to enqueue the script in the footer.
     69     *
     70     * @return void
     71     */
     72    public static function register_script( string $handle, string $src, array $deps = [], bool $in_footer = true ) {
     73        wp_register_script( $handle, self::get_script_url( $src ), $deps, ISCVERSION, $in_footer );
     74    }
     75
     76    /**
     77     * Get script URL
     78     *
     79     * @param string $src The relative script source path.
     80     *
     81     * @return string The full URL to the script, with the base URL prepended and minified if SCRIPT_DEBUG is off.
     82     */
     83    public static function get_script_url( string $src ): string {
     84        // load the minified script version if SCRIPT_DEBUG is off
     85        if ( ! SCRIPT_DEBUG && strpos( $src, '.min.js' ) === false ) {
     86            $src = str_replace( '.js', '.min.js', $src );
     87        }
     88
     89        // fix slashes
     90        return trailingslashit( ISCBASEURL ) . ltrim( $src, '/' );
     91    }
    4792}
  • image-source-control-isc/trunk/includes/image-sources/admin/scripts.php

    r3253182 r3305514  
    44
    55use ISC\Admin_Utils;
     6use ISC\Helpers;
    67
    78/**
     
    2829
    2930        if ( $screen->id === 'media_page_isc-sources' ) {
    30             wp_enqueue_script( 'isc_sources_script', ISCBASEURL . '/admin/assets/js/sources.js', [], ISCVERSION, true );
     31            Helpers::enqueue_script( 'isc_sources_script', 'admin/assets/js/sources.js' );
    3132        }
    3233
     
    3738
    3839        if ( in_array( $screen->id, [ 'upload', 'widgets', 'customize' ], true ) ) {
    39             wp_enqueue_script( 'isc_attachment_compat', ISCBASEURL . '/admin/assets/js/wp.media.view.AttachmentCompat.js', [ 'media-upload' ], ISCVERSION, true );
     40            Helpers::enqueue_script( 'isc_attachment_compat', 'admin/assets/js/wp.media.view.AttachmentCompat.js', [ 'media-upload' ] );
    4041        }
    4142    }
  • image-source-control-isc/trunk/includes/settings.php

    r3253281 r3305514  
    7070
    7171        if ( isset( $screen->id ) && $screen->id === 'settings_page_isc-settings' ) {
    72             wp_enqueue_script( 'isc_settings_script', ISCBASEURL . '/admin/assets/js/settings.js', [], ISCVERSION, true );
     72            Helpers::enqueue_script( 'isc_settings_script', 'admin/assets/js/settings.js' );
    7373        }
    7474    }
  • image-source-control-isc/trunk/includes/settings/sections/page-list.php

    r3253182 r3305514  
    1717        add_settings_field( 'source_type_list', __( 'Enable', 'image-source-control-isc' ), [ $this, 'render_field_source_type_list' ], 'isc_settings_page', 'isc_settings_section_list_below_content' );
    1818        add_settings_field( 'image_list_headline', __( 'Headline', 'image-source-control-isc' ), [ $this, 'render_field_list_headline' ], 'isc_settings_page', 'isc_settings_section_list_below_content' );
     19        add_settings_field( 'list_layout', __( 'Layout', 'image-source-control-isc' ), [ $this, 'render_field_list_layout' ], 'isc_settings_page', 'isc_settings_section_list_below_content' );
    1920        add_settings_field( 'below_content_included_images', __( 'Included images', 'image-source-control-isc' ), [ $this, 'render_field_below_content_included_images' ], 'isc_settings_page', 'isc_settings_section_list_below_content' );
    2021    }
     
    4142        $options = $this->get_options();
    4243        require_once ISCPATH . '/admin/templates/settings/page-list/headline.php';
     44    }
     45
     46    /**
     47     * Render option to define the layout of the image list
     48     */
     49    public function render_field_list_layout() {
     50        $options             = $this->get_options();
     51        $list_layout_details = ! empty( ( $options['list_layout'] ?? [] )['details'] ?? false );
     52        $is_pro_enabled      = \ISC\Plugin::is_pro();
     53        require_once ISCPATH . '/admin/templates/settings/page-list/layout.php';
    4354    }
    4455
  • image-source-control-isc/trunk/isc.php

    r3283881 r3305514  
    22/**
    33 * Plugin Name: Image Source Control Lite
    4  * Version: 3.2.0
     4 * Version: 3.3.0
    55 * Plugin URI: https://imagesourcecontrol.com/
    66 * Description: Image Source Control saves the source of an image, lists them and warns if it is missing.
     
    3030}
    3131
    32 define( 'ISCVERSION', '3.2.0' );
     32define( 'ISCVERSION', '3.3.0' );
    3333define( 'ISCNAME', 'Image Source Control' );
    3434define( 'ISCDIR', basename( __DIR__ ) );
  • image-source-control-isc/trunk/lib/composer/autoload_classmap.php

    r3283881 r3305514  
    3737    'ISC\\Pro\\Custom_Attribute_Processor' => $baseDir . '/pro/includes/Custom_Attribute_Processor.php',
    3838    'ISC\\Pro\\IPTC' => $baseDir . '/pro/includes/IPTC.php',
     39    'ISC\\Pro\\List_Layout_Details' => $baseDir . '/pro/includes/List_Layout_Details.php',
    3940    'ISC\\Settings' => $baseDir . '/includes/settings.php',
    4041    'ISC\\Settings\\Section' => $baseDir . '/includes/settings/section.php',
  • image-source-control-isc/trunk/lib/composer/autoload_static.php

    r3283881 r3305514  
    5757        'ISC\\Pro\\Custom_Attribute_Processor' => __DIR__ . '/../..' . '/pro/includes/Custom_Attribute_Processor.php',
    5858        'ISC\\Pro\\IPTC' => __DIR__ . '/../..' . '/pro/includes/IPTC.php',
     59        'ISC\\Pro\\List_Layout_Details' => __DIR__ . '/../..' . '/pro/includes/List_Layout_Details.php',
    5960        'ISC\\Settings' => __DIR__ . '/../..' . '/includes/settings.php',
    6061        'ISC\\Settings\\Section' => __DIR__ . '/../..' . '/includes/settings/section.php',
  • image-source-control-isc/trunk/public/assets/js/captions.js

    r3217506 r3305514  
    77
    88// Use values from isc_front_data if available, otherwise use default values
    9 const ISC_FONT_SIZE                 = isc_front_data.font_size || '0.9em';
    10 const ISC_BACKGROUND_COLOR          = isc_front_data.background_color || '#333';
    11 const ISC_TEXT_COLOR                = isc_front_data.text_color || '#fff';
    12 const ISC_OPACITY                   = isc_front_data.opacity || 0.70;
    13 const ISC_PADDING                   = isc_front_data.padding || '0 0.15em';
    14 const ISC_DISPLAY                   = isc_front_data.display || 'block';
    159const ISC_Z_INDEX                   = isc_front_data.z_index || '9999';
    1610const ISC_CAPTION_HORIZONTAL_MARGIN = isc_front_data.caption_horizontal_margin || 5;
    1711const ISC_CAPTION_VERTICAL_MARGIN   = isc_front_data.caption_vertical_margin || 5;
     12const ISC_STYLE_STRING = (() => {
     13    const styles = isc_front_data.caption_style || {};
     14    let styleString = '';
     15
     16    for ( const [property, value] of Object.entries(styles) ) {
     17        styleString += `${property}: ${value}; `;
     18    }
     19
     20    return styleString.trim();
     21})();
    1822
    1923/**
     
    2832            const l        = captions.length;
    2933            for ( let i = 0; i < l; i++ ) {
    30                 captions[i].setAttribute( "style", `position: absolute; font-size: ${ISC_FONT_SIZE}; background-color: ${ISC_BACKGROUND_COLOR}; color: ${ISC_TEXT_COLOR}; opacity: ${ISC_OPACITY}; padding: ${ISC_PADDING}; text-shadow: none; display: ${ISC_DISPLAY}` );
    31                 // Some themes handle the bottom padding of the attachment's div with the caption text (which is in between
    32                 // the image and the bottom border) not with the div itself. The following line set the padding on the bottom equal to the top.
    33                 captions[i].style.paddingBottom = window.getComputedStyle( captions[i] )['padding-top'];
     34                isc_update_caption_style( captions[i] );
    3435                // position the parent element (.isc-source)
    3536                isc_update_caption_position( captions[i].parentNode );
     
    5354            isc_update_captions_positions();
    5455        } );
     56
     57        /**
     58         * Monitor DOM changes to update captions
     59         */
     60        isc_setup_mutation_observer();
    5561};
     62
     63/**
     64 * Iterate through all image source captions and set basic CSS style
     65 *
     66 * This can be used to add styles to dynamically added captions, e.g., by AJAX calls
     67 */
     68function isc_update_captions_styles() {
     69    const captions = document.querySelectorAll( '.isc-source .isc-source-text' );
     70    const l        = captions.length;
     71    for ( let i = 0; i < l; i++ ) {
     72        isc_update_caption_style( captions[i] );
     73    }
     74}
     75
     76/**
     77 * Add basic CSS to a single image source caption
     78 *
     79 * @param caption image source caption that needs positioning
     80 */
     81function isc_update_caption_style( caption ) {
     82    // bail if the element already has a style attribute
     83    if ( caption.hasAttribute( 'style' ) ) {
     84        return;
     85    }
     86
     87    caption.setAttribute( "style", ISC_STYLE_STRING );
     88    // Some themes handle the bottom padding of the attachment's div with the caption text (which is in between
     89    // the image and the bottom border) not with the div itself. The following line set the padding on the bottom equal to the top.
     90    caption.style.paddingBottom = window.getComputedStyle( caption )['padding-top'];
     91}
    5692
    5793/**
     
    180216    return el.offsetHeight + parseInt( style.marginTop ) + parseInt( style.marginBottom );
    181217}
     218
     219/**
     220 * Sets up the MutationObserver to watch for dynamically added captions.
     221 */
     222function isc_setup_mutation_observer() {
     223    // Get selectors for elements to observe from PHP, default to an empty array
     224    const elementsToObserveSelectors = isc_front_data.observe_elements_selectors || [];
     225
     226    if ( elementsToObserveSelectors.length === 0 ) {
     227        return; // Do nothing if no selectors are provided
     228    }
     229
     230    const observer = new MutationObserver( ( mutationsList, observerInstance) => {
     231        let newCaptionsDetected = false;
     232        for ( const mutation of mutationsList ) {
     233            // We are interested in mutations where nodes were added.
     234            if ( mutation.type === 'childList' && mutation.addedNodes.length > 0 ) {
     235                // Iterate through each node that was added.
     236                // An AJAX response might add multiple sibling elements at once.
     237                for ( const addedNode of mutation.addedNodes ) {
     238                    // We only operate on actual HTML elements.
     239                    if ( addedNode.nodeType === Node.ELEMENT_NODE ) {
     240                        // Check for the presence of the caption text
     241                        if ( addedNode.querySelector( '.isc-source .isc-source-text' ) ) {
     242                            newCaptionsDetected = true;
     243                            // Found what we're looking for, no need to check further
     244                            // added nodes in this particular mutation.
     245                            break;
     246                        }
     247                    }
     248                }
     249            }
     250            // If we've detected new captions in any of the mutations,
     251            // we can stop checking the rest of the mutations.
     252            if ( newCaptionsDetected ) {
     253                break;
     254            }
     255        }
     256
     257        if ( newCaptionsDetected ) {
     258            console.log('ISC MutationObserver: New captions detected. Updating styles and positions.');
     259            isc_update_captions_styles();
     260            isc_update_captions_positions();
     261        }
     262    });
     263
     264    elementsToObserveSelectors.forEach( selector => {
     265        const targetNode = document.querySelector(selector);
     266        if ( targetNode ) {
     267            observer.observe( targetNode, { childList: true, subtree: true } );
     268        }
     269    } );
     270}
  • image-source-control-isc/trunk/public/public.php

    r3283881 r3305514  
    22
    33use ISC\Standard_Source;
     4use ISC\Helpers;
    45
    56/**
     
    128129        }
    129130        // inject in footer as we can only reliably position captions when the DOM is fully loaded
    130         wp_enqueue_script( 'isc_caption', plugins_url( '/assets/js/captions.js', __FILE__ ), null, ISCVERSION, true );
    131     }
    132 
     131        Helpers::enqueue_script( 'isc_caption', 'public/assets/js/captions.js' );
     132
     133        $options = $this->get_options();
     134
     135        $caption_style = [
     136            'position'         => 'absolute',
     137            'font-size'        => '0.9em',
     138            'background-color' => '#333',
     139            'color'            => '#fff',
     140            'opacity'          => '0.70',
     141            'padding'          => '0 0.15em',
     142            'text-shadow'      => 'none',
     143            'display'          => 'block',
     144        ];
     145
     146        $front_data = [
     147            'caption_position' => isset( $options['caption_position'] ) ? esc_html( $options['caption_position'] ) : '',
    133148            /**
    134              * Front-end scripts in <head /> section.
     149             * Filter: isc_public_caption_default_style
     150             * Allows to change the default caption style.
     151             *
     152             * @param array $caption_style The default caption style.
     153             * @param array $options The options array.
    135154             */
     155            'caption_style'    => apply_filters( 'isc_public_caption_default_style', $caption_style, $options ),
     156        ];
     157
     158        /**
     159         * Filter: isc_public_caption_script_options
     160         *
     161         * @param array $front_data The data to be localized.
     162         * @param array $options The options array.
     163         */
     164        $filtered_front_data = apply_filters( 'isc_public_caption_script_options', $front_data, $options );
     165
     166        wp_localize_script(
     167            'isc_caption',
     168            'isc_front_data',
     169            $filtered_front_data
     170        );
     171    }
     172
     173    /**
     174     * Front-end scripts in <head /> section.
     175     */
    136176    public function front_head() {
    137177        // don’t add the script on AMP pages
     
    142182        $options = $this->get_options();
    143183        ?>
    144             <script>
    145             /* <![CDATA[ */
    146                 var isc_front_data =
    147                 {
    148                     caption_position : '<?php echo esc_html( $options['caption_position'] ); ?>',
    149                 }
    150             /* ]]> */
    151             </script>
    152184            <style>
    153185                .isc-source { position: relative; display: inline-block; line-height: initial; }
     
    513545        }
    514546
    515         $options  = $this->get_options();
    516         $headline = $options['image_list_headline'];
    517 
    518547        ob_start();
    519548
    520549        ?>
    521             <p class="isc_image_list_title"><?php echo esc_html( $headline ); ?></p>
    522550            <ul class="isc_image_list">
    523551        <?php
     
    947975     *
    948976     * @param string $content content of the source box, i.e., list of sources.
    949      */
    950     public function render_image_source_box( $content = null ) {
     977     * @param bool   $create_placeholder if true, create a placeholder box without content.
     978     */
     979    public function render_image_source_box( string $content = '', bool $create_placeholder = false ): string {
     980        $options  = $this->get_options();
     981        $headline = $options['image_list_headline'];
     982
    951983        ob_start();
    952984        require ISCPATH . 'public/views/image-source-box.php';
     
    954986        ISC_Log::log( 'finished creating image source box' );
    955987
    956         return ob_get_clean();
     988        /**
     989         * Filter: isc_render_image_source_box
     990         * allow to modify the output of the image source box
     991         *
     992         * @param string $content content of the source box.
     993         * @param string $headline headline of the source box.
     994         * @param bool   $create_placeholder if true, create a placeholder box without content.
     995         */
     996        return apply_filters( 'isc_render_image_source_box', ob_get_clean(), $content, $headline, $create_placeholder );
    957997    }
    958998
  • image-source-control-isc/trunk/public/views/image-source-box.php

    r2414060 r3305514  
    66 *
    77 * @var string $content list of image source or other content.
     8 * @var string $headline headline for the image list.
     9 * @var bool   $create_placeholder whether to create a placeholder or not.
    810 */
     11
    912?>
    10 <div class="isc_image_list_box"><?php echo isset( $content ) ? $content : ''; ?></div>
     13<div class="isc_image_list_box"><?php if ( ! $create_placeholder ) : ?>
     14    <p class="isc_image_list_title"><?php echo esc_html( $headline ); ?></p>
     15    <?php echo $content; ?>
     16<?php endif; ?></div>
  • image-source-control-isc/trunk/readme.txt

    r3289894 r3305514  
    44Requires at least: 6.0
    55Tested up to: 6.8
    6 Stable tag: 3.2.0
     6Stable tag: 3.3.0
    77Requires PHP: 7.4
    88License: GPLv3 or later
     
    8989
    9090Extended compatibility with Elementor, Avada, WP Bakery, and other page builders
    91 as well as with plugins like WPML, Kadence Blocks, Kadence Related Content Carousel, and Lightbox Gallery.
     91as well as with plugins like WPML, Kadence Blocks, Kadence Related Content Carousel, Lightbox Gallery, and JetEngine.
    9292
    9393[See Pricing](https://imagesourcecontrol.com/pricing/?utm_source=wporg&utm_medium=link&utm_campaign=pricing).
     
    164164== Changelog ==
    165165
     166= 3.3.0 =
     167
     168- Feature (Pro): Layout option to collapse the Per-page list below the content by default and only open it on click
     169- Improvement: Minified JavaScript files in frontend and backend for faster load times
     170- Improvement (Pro): Consider the site_icon image as "used" and don’t list it under Unused Images
     171- Improvement (Pro): Support for JetEngine AJAX-loaded content
     172- Improvement (Pro): The expand-on-click caption option is more stable and should not create random line breaks
     173- Fix (Pro): Honor the Elementor compatibility option and disable the feature, if the option is not checked
     174- Fix (Pro): Made a string on the Indexer page translatable
     175- Dev: The main caption CSS can be customized via the `isc_public_caption_default_style` filter
     176- Dev: The new `isc_update_captions_styles()` JavaScript function allows developers to update the caption styles in the frontend. E.g., useful for dynamically loaded content
     177- Dev (Pro): Improved handling nested output buffer calls to prevent issues with other plugins; here it was WPBakery
     178- Dev (Pro): Use the `isc_unused_images_ids_considered_used` filter to add IDs of images that should not show as unused images
     179
    166180= 3.2.0 =
     181
     182April 29th, 2025
    167183
    168184- Feature: You can use the option “Images only” to disable features for non-images in the media library, e.g., PDF files
Note: See TracChangeset for help on using the changeset viewer.