晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。   林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。   见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝)   既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。   南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。 .
Prv8 Shell
Server : Apache
System : Linux srv.rainic.com 4.18.0-553.47.1.el8_10.x86_64 #1 SMP Wed Apr 2 05:45:37 EDT 2025 x86_64
User : rainic ( 1014)
PHP Version : 7.4.33
Disable Function : exec,passthru,shell_exec,system
Directory :  /home/rainic/public_html/oldTZh/wp-content/plugins/persian-elementor/widget/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/rainic/public_html/oldTZh/wp-content/plugins/persian-elementor/widget/neshan-map.php
<?php
/**
 * Neshan Map Widget for Elementor
 */

if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly
}

// Check if feature is enabled in settings
$options = get_option('persian_elementor', []);
if (!($options['efa-neshan-map'] ?? true)) {
    return; // Don't load if feature is disabled
}

// Register scripts and styles for all contexts: frontend, editor, and preview
add_action('wp_enqueue_scripts', 'register_neshan_map_assets');
add_action('elementor/editor/before_enqueue_scripts', 'register_neshan_map_assets');
add_action('elementor/preview/enqueue_scripts', 'register_neshan_map_assets');

// Enqueue in editor context
add_action('elementor/editor/after_enqueue_scripts', 'enqueue_neshan_map_assets');
add_action('elementor/editor/after_enqueue_styles', 'enqueue_neshan_map_styles');

// Enqueue in preview context
add_action('elementor/preview/enqueue_scripts', 'enqueue_neshan_map_assets');
add_action('elementor/preview/enqueue_styles', 'enqueue_neshan_map_styles');

/**
 * Register the Neshan Map assets
 */
function register_neshan_map_assets() {
    wp_register_style('neshan-map-sdk', 'https://static.neshan.org/sdk/mapboxgl/v1.13.2/neshan-sdk/v1.0.8/index.css', [], '1.0.8');
    wp_register_script('neshan-map-sdk', 'https://static.neshan.org/sdk/mapboxgl/v1.13.2/neshan-sdk/v1.0.8/index.js', [], '1.0.8', true);
    
    // Register custom editor script to initialize map in editor
    wp_register_script(
        'neshan-map-editor', 
        plugins_url('../assets/js/neshan-map-editor.js', __FILE__),
        ['jquery', 'neshan-map-sdk'],
        '1.0.0',
        true
    );
}

/**
 * Enqueue the Neshan Map scripts
 */
function enqueue_neshan_map_assets() {
    wp_enqueue_script('neshan-map-sdk');
    wp_enqueue_script('neshan-map-editor');
}

/**
 * Enqueue the Neshan Map styles
 */
function enqueue_neshan_map_styles() {
    wp_enqueue_style('neshan-map-sdk');
}

// Register widget
add_action('elementor/widgets/register', 'register_persian_elementor_neshan_map_widget');

function register_persian_elementor_neshan_map_widget($widgets_manager) {
    class Persian_Elementor_Neshan_Map_Widget extends \Elementor\Widget_Base {
        
        public function get_name() {
            return 'neshan_map';
        }
        
        public function get_title() {
            return esc_html__('نقشه نشان', 'persian-elementor');
        }
        
        public function get_icon() {
            // Use the SVG file as background image
            add_action('elementor/editor/after_enqueue_styles', [$this, 'enqueue_neshan_icon_styles']);
            return 'neshan-map-icon';
        }
        
        /**
         * Enqueue styles for the custom Neshan icon
         */
        public function enqueue_neshan_icon_styles() {
            $icon_url = plugins_url('../assets/images/neshan.svg', __FILE__);
            
            // Output inline CSS for the custom icon
            echo '<style>
                .elementor-element .neshan-map-icon::after {
                    content: "";
                    background-image: url(' . esc_url($icon_url) . ');
                    background-size: contain;
                    background-repeat: no-repeat;
                    background-position: center center;
                    width: 28px;
                    height: 28px;
                    display: block;
                    margin: 0 auto;
                }
                [data-elementor-type="kit"] .elementor-element .neshan-map-icon::after,
                .elementor-panel-menu-item .neshan-map-icon::after {
                    filter: brightness(0) invert(1);
                }
            </style>';
        }
        
        public function get_categories() {
            return ['basic'];
        }
        
        public function get_keywords() {
            return ['map', 'neshan', 'نقشه', 'نشان', 'مکان'];
        }
        
        protected function register_controls() {
            // Map Settings Section (Combined API and Map settings)
            $this->start_controls_section(
                'section_map',
                [
                    'label' => esc_html__('تنظیمات نقشه', 'persian-elementor'),
                ]
            );
            
            $this->add_control(
                'api_key',
                [
                    'label' => esc_html__('کلید API نشان', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::TEXT,
                    'description' => sprintf(
                        esc_html__('کلید API نشان خود را وارد کنید. برای دریافت کلید به %s مراجعه کنید.', 'persian-elementor'),
                        '<a href="https://platform.neshan.org/panel/api-key" target="_blank">پنل نشان</a>'
                    ),
                    'placeholder' => esc_html__('web.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'persian-elementor'), // Added placeholder
                    'default' => '', // Changed default to empty string
                    'separator' => 'after',
                ]
            );
            
            $this->add_control(
                'map_latitude',
                [
                    'label' => esc_html__('عرض جغرافیایی', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::TEXT,
                    'default' => '35.699789639952414',
                    'placeholder' => '35.699789639952414',
                ]
            );
            
            $this->add_control(
                'map_longitude',
                [
                    'label' => esc_html__('طول جغرافیایی', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::TEXT,
                    'default' => '51.33748508581425',
                    'placeholder' => '51.33748508581425',
                ]
            );
            
            $this->add_control(
                'map_zoom',
                [
                    'label' => esc_html__('بزرگنمایی نقشه', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::SLIDER,
                    'default' => [
                        'size' => 15,
                    ],
                    'range' => [
                        'px' => [
                            'min' => 2,
                            'max' => 21,
                            'step' => 1,
                        ],
                    ],
                ]
            );
            
            $this->add_control(
                'map_type',
                [
                    'label' => esc_html__('نوع نقشه', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::SELECT,
                    'default' => 'neshanVector',
                    'options' => [
                        'neshanVector' => esc_html__('برداری روز', 'persian-elementor'),
                        'neshanVectorNight' => esc_html__('برداری شب', 'persian-elementor'),
                        'neshanRaster' => esc_html__('پیکسلی روز', 'persian-elementor'),
                        'neshanRasterNight' => esc_html__('پیکسلی شب', 'persian-elementor'),
                    ],
                ]
            );
            
            $this->add_control(
                'show_poi',
                [
                    'label' => esc_html__('نمایش مکان‌های منتخب', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::SWITCHER,
                    'label_on' => esc_html__('بله', 'persian-elementor'),
                    'label_off' => esc_html__('خیر', 'persian-elementor'),
                    'default' => 'yes',
                ]
            );
            
            $this->add_control(
                'show_traffic',
                [
                    'label' => esc_html__('نمایش ترافیک', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::SWITCHER,
                    'label_on' => esc_html__('بله', 'persian-elementor'),
                    'label_off' => esc_html__('خیر', 'persian-elementor'),
                    'default' => '',
                ]
            );
            
            $this->end_controls_section();
            
            // Style Settings Tab
            $this->start_controls_section(
                'section_style',
                [
                    'label' => esc_html__('استایل نقشه', 'persian-elementor'),
                    'tab' => \Elementor\Controls_Manager::TAB_STYLE,
                ]
            );
            
            // Move marker color to Style tab
            $this->add_control(
                'marker_heading',
                [
                    'label' => esc_html__('نشانگر', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::HEADING,
                ]
            );
            
            $this->add_control(
                'marker_color',
                [
                    'label' => esc_html__('رنگ نشانگر', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::COLOR,
                    'default' => '#FF8330',
                    'selectors' => [
                        '{{WRAPPER}} .neshan-map-container .mapboxgl-marker svg g g[fill]' => 'fill: {{VALUE}} !important;',
                    ],
                ]
            );
            
            $this->add_control(
                'map_dimensions_heading',
                [
                    'label' => esc_html__('ابعاد', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::HEADING,
                    'separator' => 'before',
                ]
            );
            
            $this->add_responsive_control(
                'map_height',
                [
                    'label' => esc_html__('ارتفاع نقشه', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::SLIDER,
                    'size_units' => ['px', 'vh', '%'],
                    'range' => [
                        'px' => [
                            'min' => 100,
                            'max' => 1000,
                            'step' => 10,
                        ],
                        'vh' => [
                            'min' => 10,
                            'max' => 100,
                        ],
                        '%' => [
                            'min' => 10,
                            'max' => 100,
                        ],
                    ],
                    'default' => [
                        'size' => 400,
                        'unit' => 'px',
                    ],
                    'selectors' => [
                        '{{WRAPPER}} .neshan-map-container' => 'height: {{SIZE}}{{UNIT}} !important; min-height: {{SIZE}}{{UNIT}} !important;',
                    ],
                    'render_type' => 'template',
                    'frontend_available' => true,
                ]
            );
            
            $this->add_responsive_control(
                'map_width',
                [
                    'label' => esc_html__('عرض نقشه', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::SLIDER,
                    'size_units' => ['px', 'vw', '%'],
                    'range' => [
                        'px' => [
                            'min' => 100,
                            'max' => 1500,
                            'step' => 10,
                        ],
                        'vw' => [
                            'min' => 10,
                            'max' => 100,
                        ],
                        '%' => [
                            'min' => 10,
                            'max' => 100,
                        ],
                    ],
                    'default' => [
                        'size' => 100,
                        'unit' => '%',
                    ],
                    'selectors' => [
                        '{{WRAPPER}} .neshan-map-container' => 'width: {{SIZE}}{{UNIT}} !important; min-width: {{SIZE}}{{UNIT}} !important;',
                    ],
                    'render_type' => 'template',
                    'frontend_available' => true,
                ]
            );
            
            $this->add_control(
                'map_style_heading',
                [
                    'label' => esc_html__('استایل ظاهری', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::HEADING,
                    'separator' => 'before',
                ]
            );
            
            $this->add_control(
                'map_border_radius',
                [
                    'label' => esc_html__('گردی گوشه‌ها', 'persian-elementor'),
                    'type' => \Elementor\Controls_Manager::DIMENSIONS,
                    'size_units' => ['px', '%', 'em'],
                    'selectors' => [
                        '{{WRAPPER}} .neshan-map-container' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
                    ],
                ]
            );
            
            $this->add_group_control(
                \Elementor\Group_Control_Border::get_type(),
                [
                    'name' => 'map_border',
                    'selector' => '{{WRAPPER}} .neshan-map-container',
                    'separator' => 'before',
                ]
            );
            
            $this->add_group_control(
                \Elementor\Group_Control_Box_Shadow::get_type(),
                [
                    'name' => 'map_box_shadow',
                    'selector' => '{{WRAPPER}} .neshan-map-container',
                ]
            );
            
            $this->end_controls_section();
        }
        
        protected function render() {
            $settings = $this->get_settings_for_display();
            
            // Get values from settings
            $api_key = !empty($settings['api_key']) ? trim($settings['api_key']) : ''; // Trim whitespace
            $marker_color = !empty($settings['marker_color']) ? $settings['marker_color'] : '#FF8330';
            $latitude = !empty($settings['map_latitude']) ? $settings['map_latitude'] : '35.699789639952414';
            $longitude = !empty($settings['map_longitude']) ? $settings['map_longitude'] : '51.33748508581425';
            $map_zoom = isset($settings['map_zoom']['size']) ? (int) $settings['map_zoom']['size'] : 15;
            $map_type = !empty($settings['map_type']) ? $settings['map_type'] : 'neshanVector';
            $show_poi = ($settings['show_poi'] === 'yes');
            $show_traffic = ($settings['show_traffic'] === 'yes');
            
            // Calculate heights and widths - prioritize the responsive controls if set
            $height = isset($settings['map_height']['size']) ? $settings['map_height']['size'] . $settings['map_height']['unit'] : '400px';
            $width = isset($settings['map_width']['size']) ? $settings['map_width']['size'] . $settings['map_width']['unit'] : '100%';
            
            // Check if API key is missing or is the default placeholder (or the old default)
            if (empty($api_key) || $api_key === 'web.') { // Keep check for 'web.' for safety/backward compatibility
                // Display placeholder message if API key is missing
                ?>
                <div class="neshan-map-api-key-missing" style="height: <?php echo esc_attr($height); ?>; width: <?php echo esc_attr($width); ?>; display: flex; align-items: center; justify-content: center; background-color: #f0f0f0; border: 1px dashed #ccc; text-align: center; padding: 20px; box-sizing: border-box;">
                    <?php esc_html_e('برای نمایش نقشه باید کلید API نشان را در تنظیمات ویجت وارد کنید.', 'persian-elementor'); ?>
                </div>
                <?php
                return; // Stop further execution for this widget instance
            }
            
            // Create truly unique ID for this map instance
            $widget_id = $this->get_id();
            $unique_id = uniqid('neshanMap_');
            $map_id = 'neshanMap_' . $widget_id . '_' . $unique_id;
            
            // Always enqueue in frontend context (already done for editor and preview)
            if (!is_admin()) {
                wp_enqueue_style('neshan-map-sdk');
                wp_enqueue_script('neshan-map-sdk');
            }
            
            // Use Elementor's render attribute system instead of hardcoding attributes
            $this->add_render_attribute('map-container', [
                'class' => 'neshan-map-container',
                'id' => $map_id,
                'data-widget-id' => $widget_id,
                'data-instance-id' => $unique_id,
                'data-api-key' => $api_key,
                'data-lat' => $latitude,
                'data-lng' => $longitude,
                'data-marker-color' => $marker_color,
                'data-height' => $height,
                'data-width' => $width,
                'data-zoom' => $map_zoom,
                'data-map-type' => $map_type,
                'data-poi' => $show_poi ? 'true' : 'false',
                'data-traffic' => $show_traffic ? 'true' : 'false',
                'style' => 'height:' . $height . '; width:' . $width . ';', // Style applied here too
            ]);
            
            // The map container with all necessary data attributes
            ?>
            <div <?php echo $this->get_render_attribute_string('map-container'); ?>></div>

            <style>
                /* Instance-specific selectors to prevent conflicts */
                #<?php echo esc_attr($map_id); ?> {
                    overflow: hidden;
                    /* Height and width are now primarily set via inline style from add_render_attribute */
                    /* height: <?php echo esc_attr($height); ?> !important; 
                    min-height: <?php echo esc_attr($height); ?> !important;
                    width: <?php echo esc_attr($width); ?> !important;
                    min-width: <?php echo esc_attr($width); ?> !important; */
                }
                #<?php echo esc_attr($map_id); ?> a {
                    text-decoration: none !important;
                    border: none;
                    padding: 0;
                    margin: 0;
                }
                #<?php echo esc_attr($map_id); ?> a:hover,
                #<?php echo esc_attr($map_id); ?> a:active {
                    text-decoration: none !important;
                    background: none;
                    border: none;
                    padding: 0;
                    margin: 0;
                }
                
                /* Force visible in editor - instance specific */
                .elementor-editor-active #<?php echo esc_attr($map_id); ?> {
                    position: relative;
                    z-index: 1;
                }
            </style>

            <script type="text/javascript">
                (function() {
                    function initMap_<?php echo esc_js($unique_id); ?>() {
                        // Check if SDK is loaded
                        if (typeof nmp_mapboxgl === 'undefined') {
                            console.warn('Neshan SDK (nmp_mapboxgl) not loaded yet, retrying...');
                            setTimeout(initMap_<?php echo esc_js($unique_id); ?>, 300);
                            return;
                        }
                        
                        const mapContainer = document.getElementById('<?php echo esc_js($map_id); ?>');
                        // Ensure container exists and hasn't been initialized already
                        if (!mapContainer || mapContainer.neshanMapInitialized) {
                            // console.log('Map container <?php echo esc_js($map_id); ?> not found or already initialized.');
                            return;
                        }
                        
                        try {
                            // Get data from DOM - instance specific
                            const apiKey = mapContainer.getAttribute('data-api-key');
                            // Double check API key here in JS as well, though PHP check should prevent this script from running if missing
                            if (!apiKey || apiKey === 'web.') { // Keep check for 'web.' for safety/backward compatibility
                                console.error('Neshan API Key is missing for map: <?php echo esc_js($map_id); ?>');
                                mapContainer.innerHTML = '<p style="padding:20px; text-align:center;">' + <?php echo json_encode(esc_html__('خطا: کلید API نشان یافت نشد.', 'persian-elementor')); ?> + '</p>';
                                return;
                            }

                            const latitude = parseFloat(mapContainer.getAttribute('data-lat'));
                            const longitude = parseFloat(mapContainer.getAttribute('data-lng'));
                            const markerColor = mapContainer.getAttribute('data-marker-color');
                            const mapHeight = mapContainer.getAttribute('data-height'); // Already applied via style
                            const mapWidth = mapContainer.getAttribute('data-width');   // Already applied via style
                            const mapZoom = parseInt(mapContainer.getAttribute('data-zoom') || '15');
                            const mapTypeStr = mapContainer.getAttribute('data-map-type') || 'neshanVector';
                            const poi = mapContainer.getAttribute('data-poi') === 'true';
                            const traffic = mapContainer.getAttribute('data-traffic') === 'true';
                            
                            // Apply dimensions explicitly (redundant if style attribute works, but safe fallback)
                            // mapContainer.style.height = mapHeight;
                            // mapContainer.style.minHeight = mapHeight;
                            // mapContainer.style.width = mapWidth;
                            // mapContainer.style.minWidth = mapWidth;
                            
                            // Location coordinates [lng, lat] for MapboxGL
                            const mapCenterLocation = [longitude, latitude]; // Correct order for MapboxGL
                            
                            // Create map for this specific instance
                            const map = new nmp_mapboxgl.Map({
                                container: mapContainer,
                                mapKey: apiKey,
                                mapType: nmp_mapboxgl.Map.mapTypes[mapTypeStr] || nmp_mapboxgl.Map.mapTypes.neshanVector, // Fallback type
                                zoom: mapZoom,
                                pitch: 0,
                                center: mapCenterLocation, // Use directly
                                minZoom: 2,
                                maxZoom: 21,
                                trackResize: true,
                                poi: poi,
                                traffic: traffic,
                                mapTypeControllerStatus: {
                                    show: true,
                                    position: 'bottom-right'
                                }
                            });
                            
                            // Add marker
                            new nmp_mapboxgl.Marker({
                                color: markerColor,
                                draggable: false
                            })
                            .setLngLat(mapCenterLocation) // Use directly
                            .addTo(map);
                            
                            // Add user location control
                            map.addControl(new nmp_mapboxgl.GeolocateControl({
                                positionOptions: {enableHighAccuracy: true},
                                trackUserLocation: true,
                                showUserHeading: true
                            }));
                            
                            // Mark as initialized with instance-specific flag
                            mapContainer.neshanMapInitialized = true;
                            mapContainer.setAttribute('data-initialized', 'true');
                            // console.log('Neshan map initialized: <?php echo esc_js($map_id); ?>');

                        } catch (e) {
                            console.error('Error initializing Neshan map (<?php echo esc_js($map_id); ?>):', e);
                             mapContainer.innerHTML = '<p style="padding:20px; text-align:center;">' + <?php echo json_encode(esc_html__('خطا در بارگذاری نقشه رخ داد. لطفاً کنسول مرورگر را بررسی کنید.', 'persian-elementor')); ?> + '</p>';
                        }
                    }
                    
                    // Initialize map when page loads or when element becomes visible (for tabs, accordions etc.)
                    function scheduleMapInit_<?php echo esc_js($unique_id); ?>() {
                         const mapContainer = document.getElementById('<?php echo esc_js($map_id); ?>');
                         if (mapContainer && mapContainer.offsetParent !== null) { // Check if visible
                             initMap_<?php echo esc_js($unique_id); ?>();
                         } else {
                             // If not visible yet, check again later or use IntersectionObserver
                             // console.log('Map <?php echo esc_js($map_id); ?> not visible yet, delaying init.');
                             setTimeout(scheduleMapInit_<?php echo esc_js($unique_id); ?>, 500); 
                         }
                    }

                    if (document.readyState === 'loading') {
                        document.addEventListener('DOMContentLoaded', scheduleMapInit_<?php echo esc_js($unique_id); ?>);
                    } else {
                        scheduleMapInit_<?php echo esc_js($unique_id); ?>();
                    }

                    // Also try initializing if the element enters viewport (useful for dynamic content)
                    if ('IntersectionObserver' in window) {
                        const observer = new IntersectionObserver((entries) => {
                            entries.forEach(entry => {
                                if (entry.isIntersecting) {
                                    initMap_<?php echo esc_js($unique_id); ?>();
                                    observer.unobserve(entry.target); // Initialize only once
                                }
                            });
                        }, { threshold: 0.1 }); // Trigger when 10% visible

                        const target = document.getElementById('<?php echo esc_js($map_id); ?>');
                        if (target) {
                            observer.observe(target);
                        }
                    }

                })();
            </script>
            <?php
        }
        
        /**
         * Render map in Elementor editor
         * This method uses JS template to render the map in the editor preview
         */
        protected function content_template() {
            ?>
            <#
            // Get values from settings
            var apiKey = settings.api_key ? settings.api_key.trim() : ''; // Trim whitespace
            var markerColor = settings.marker_color ? settings.marker_color : '#FF8330';
            var latitude = settings.map_latitude ? settings.map_latitude : '35.699789639952414';
            var longitude = settings.map_longitude ? settings.map_longitude : '51.33748508581425';
            var mapZoom = settings.map_zoom && settings.map_zoom.size ? parseInt(settings.map_zoom.size) : 15;
            var mapType = settings.map_type ? settings.map_type : 'neshanVector';
            var showPoi = settings.show_poi === 'yes';
            var showTraffic = settings.show_traffic === 'yes';
            
            // Calculate heights and widths
            var height = settings.map_height && settings.map_height.size ? settings.map_height.size + settings.map_height.unit : '400px';
            var width = settings.map_width && settings.map_width.size ? settings.map_width.size + settings.map_width.unit : '100%';
            
            // Check if API key is missing or is the default placeholder (or the old default)
            if (!apiKey || apiKey === 'web.') { // Keep check for 'web.' for safety/backward compatibility
                #>
                <div class="neshan-map-api-key-missing elementor-nerd-box" style="height: {{ height }}; width: {{ width }}; display: flex; align-items: center; justify-content: center; text-align: center; padding: 20px; box-sizing: border-box;">
                    <div class="elementor-nerd-box-message">
                        <?php esc_html_e('برای نمایش نقشه باید کلید API نشان را در تنظیمات ویجت وارد کنید.', 'persian-elementor'); ?>
                    </div>
                </div>
                <#
            } else {
                // Create unique ID for this map instance in editor
                var widgetId = view.getID();
                // Generate a more robust unique ID for the editor context
                var uniqueId = widgetId + '_' + Math.random().toString(36).substring(2, 9); 
                var mapId = 'neshanMap_' + uniqueId + '_editor';
                
                // Use Elementor's JS render attribute system
                view.addRenderAttribute('map-container', {
                    'class': 'neshan-map-container elementor-neshan-map-editor',
                    'id': mapId,
                    'data-widget-id': widgetId,
                    'data-instance-id': uniqueId,
                    'data-api-key': apiKey,
                    'data-lat': latitude,
                    'data-lng': longitude,
                    'data-marker-color': markerColor,
                    'data-height': height, // Pass height/width for JS init
                    'data-width': width,
                    'data-zoom': mapZoom,
                    'data-map-type': mapType,
                    'data-poi': showPoi ? 'true' : 'false',
                    'data-traffic': showTraffic ? 'true' : 'false',
                    'style': 'height:' + height + '; width:' + width + ';', // Apply style directly
                });
                #>
                
                <div {{{ view.getRenderAttributeString('map-container') }}}>
                    <div class="elementor-custom-embed-play" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 2; pointer-events: none;">
                        <i class="eicon-loading eicon-animation-spin" aria-hidden="true"></i>
                    </div>
                </div>
                
                <style>
                    /* Styles specific to the editor map instance */
                    #{{ mapId }} {
                        overflow: hidden;
                        position: relative; /* Needed for overlay and spinner */
                        /* height/width set by inline style */
                    }
                    #{{ mapId }} a {
                        text-decoration: none !important; border: none; padding: 0; margin: 0;
                    }
                    #{{ mapId }} a:hover,
                    #{{ mapId }} a:active {
                        text-decoration: none !important; background: none; border: none; padding: 0; margin: 0;
                    }
                    /* Editor overlay to prevent interaction issues */
                    .elementor-editor-active .elementor-neshan-map-editor:before {
                        content: "";
                        position: absolute;
                        top: 0; left: 0; right: 0; bottom: 0;
                        background: rgba(0,0,0,0.05); /* Slight overlay */
                        z-index: 10; /* Above map controls but below spinner */
                        pointer-events: none; /* Allow clicks to pass through to Elementor controls */
                    }
                     /* Hide spinner once map is initialized */
                    #{{ mapId }}[data-initialized="true"] .elementor-custom-embed-play {
                        display: none;
                    }
                </style>
                
                <# 
                // Use a self-executing function to avoid polluting global scope in editor
                (function() {
                    var currentMapId = mapId; // Capture mapId in this scope
                    var currentUniqueId = uniqueId; // Capture uniqueId

                    // Define the initialization function specific to this instance
                    function initEditorMap() {
                        if (typeof nmp_mapboxgl === 'undefined') {
                            // console.warn('Editor: Neshan SDK not ready for ' + currentMapId + ', retrying...');
                            setTimeout(initEditorMap, 300);
                            return;
                        }

                        var $mapContainer = jQuery('#' + currentMapId);
                        if (!$mapContainer.length || $mapContainer.data('neshanMapInitialized')) {
                            // console.log('Editor: Map container ' + currentMapId + ' not found or already initialized.');
                            return; // Already initialized or container not found
                        }

                        var mapContainerEl = $mapContainer[0];

                        try {
                            // Get data from attributes again (safer in editor context)
                            var apiKey = $mapContainer.data('api-key');
                            if (!apiKey || apiKey === 'web.') { // Keep check for 'web.' for safety/backward compatibility
                                console.error('Editor: Neshan API Key missing for map: ' + currentMapId);
                                $mapContainer.html('<p style="padding:20px; text-align:center;">' + <?php echo json_encode(esc_html__('خطا: کلید API نشان یافت نشد.', 'persian-elementor')); ?> + '</p>');
                                return;
                            }
                            var latitude = parseFloat($mapContainer.data('lat'));
                            var longitude = parseFloat($mapContainer.data('lng'));
                            var markerColor = $mapContainer.data('marker-color');
                            var mapZoom = parseInt($mapContainer.data('zoom') || '15');
                            var mapTypeStr = $mapContainer.data('map-type') || 'neshanVector';
                            var poi = $mapContainer.data('poi') === true; // data() might convert 'true'/'false'
                            var traffic = $mapContainer.data('traffic') === true;
                            
                            // Location coordinates [lng, lat]
                            var mapCenterLocation = [longitude, latitude];
                            
                            // Create map
                            var map = new nmp_mapboxgl.Map({
                                container: mapContainerEl,
                                mapKey: apiKey,
                                mapType: nmp_mapboxgl.Map.mapTypes[mapTypeStr] || nmp_mapboxgl.Map.mapTypes.neshanVector,
                                zoom: mapZoom,
                                pitch: 0,
                                center: mapCenterLocation,
                                minZoom: 2,
                                maxZoom: 21,
                                trackResize: true, // Important for editor resizing
                                poi: poi,
                                traffic: traffic,
                                mapTypeControllerStatus: {
                                    show: true,
                                    position: 'bottom-right'
                                }
                            });
                            
                            // Add marker
                            new nmp_mapboxgl.Marker({
                                color: markerColor,
                                draggable: false // Usually false in editor preview
                            })
                            .setLngLat(mapCenterLocation)
                            .addTo(map);

                            // Add a slight delay before marking as initialized to allow rendering
                            setTimeout(function() {
                                $mapContainer.data('neshanMapInitialized', true);
                                $mapContainer.attr('data-initialized', 'true'); // For CSS selector
                                // console.log('Editor: Neshan map initialized: ' + currentMapId);
                            }, 100); 

                        } catch (e) {
                            console.error('Error initializing Neshan map in editor (' + currentMapId + '):', e);
                            $mapContainer.html('<p style="padding:20px; text-align:center;">' + <?php echo json_encode(esc_html__('خطا در بارگذاری نقشه در ویرایشگر.', 'persian-elementor')); ?> + '</p>');
                        }
                    }

                    // Use Elementor frontend hooks for initialization in the editor
                    if (elementorFrontend && elementorFrontend.hooks) {
                         // Use a timeout to ensure the element is fully rendered in the DOM
                         // Especially important when adding the widget for the first time
                        setTimeout(function() {
                            initEditorMap();
                        }, 150); // Small delay

                        // Re-initialize on view refresh (e.g., after changing settings)
                        elementor.channels.editor.on('change', function( controlView, model ) {
                            // Check if the changed control belongs to this widget instance
                            if ( model.cid === view.cid ) {
                                var $mapContainer = jQuery('#' + currentMapId);
                                if ($mapContainer.length) {
                                     // Clear previous instance if exists (simple way)
                                     $mapContainer.empty().removeData('neshanMapInitialized').removeAttr('data-initialized');
                                     // Re-render the container (might be needed if attributes changed significantly)
                                     // This part is tricky, ideally Elementor handles re-rendering the template.
                                     // For now, just re-run init after a short delay.
                                     setTimeout(initEditorMap, 150); 
                                }
                            }
                        });

                    } else {
                        // Fallback if hooks aren't available (less likely)
                        jQuery(document).ready(initEditorMap);
                    }
                })();
                #>
            <# 
            } // End else block (API key exists)
            #>
            <?php
        }
    }
    
    $widgets_manager->register(new Persian_Elementor_Neshan_Map_Widget());
}

haha - 2025