PageSourceSearch

https://soringoldis.com/wp-content/plugins/persian-elementor/assets/js/datepicker-init.js?ver=2.8.3

js soringoldis.com collected 2026-10-08 11:04:56 UTC 10,900 bytes, 312 lines download raw bytes

1(function () {
2    'use strict';
3
4    // Track active calendar instances
5    var activeCalendars = new WeakMap();
6    var calendarPopup = null;
7    var currentInput = null;
8    var lastOpenTime = 0; // Track when calendar was opened
9
10    /**
11     * Create popup container for calendar
12     */
13    function getOrCreatePopup() {
14        if (!calendarPopup) {
15            calendarPopup = document.createElement('div');
16            calendarPopup.className = 'persian-date-popup';
17            calendarPopup.style.cssText = 'position:absolute;z-index:999999;display:none;';
18            document.body.appendChild(calendarPopup);
19        }
20        return calendarPopup;
21    }
22
23    /**
24     * Position popup near input element
25     */
26    function positionPopup(input) {
27        var popup = getOrCreatePopup();
28        var rect = input.getBoundingClientRect();
29        var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
30        var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
31
32        var targetTop = rect.bottom + scrollTop + 5;
33        var targetLeft = rect.left + scrollLeft;
34
35        // Adjust position relative to parent if inside a popup modal
36        var parent = popup.parentNode;
37        var parentRect = null;
38        if (parent && parent !== document.body) {
39            parentRect = parent.getBoundingClientRect();
40            targetTop -= (parentRect.top + scrollTop);
41            targetLeft -= (parentRect.left + scrollLeft);
42        }
43
44        popup.style.top = targetTop + 'px';
45        popup.style.left = targetLeft + 'px';
46
47        // Check if popup goes off screen
48        setTimeout(function () {
49            var popupRect = popup.getBoundingClientRect();
50            if (popupRect.right > window.innerWidth) {
51                var newLeft = window.innerWidth - popupRect.width - 10 + scrollLeft;
52                if (parentRect) {
53                    newLeft -= (parentRect.left + scrollLeft);
54                }
55                popup.style.left = newLeft + 'px';
56            }
57            if (popupRect.bottom > window.innerHeight) {
58                var newTop = rect.top + scrollTop - popupRect.height - 5;
59                if (parentRect) {
60                    newTop -= (parentRect.top + scrollTop);
61                }
62                popup.style.top = newTop + 'px';
63            }
64        }, 0);
65    }
66
67
68    /**
69     * Check if calendar is open
70     */
71    function isCalendarOpen() {
72        return calendarPopup && calendarPopup.style.display !== 'none';
73    }
74
75    /**
76     * Show calendar popup
77     */
78    function showCalendar(input) {
79        if (typeof PersianElementorCalendar === 'undefined') {
80            console.error('Persian Elementor: PersianElementorCalendar not loaded');
81            return;
82        }
83
84        // If already open for a different input, close it first
85        if (isCalendarOpen() && currentInput !== input) {
86            hideCalendar();
87        }
88
89        // If already open for this input, don't reopen
90        if (isCalendarOpen() && currentInput === input) {
91            return;
92        }
93
94        var popup = getOrCreatePopup();
95        
96        // Find parent popup to avoid triggering click-outside close handlers
97        var parentPopup = input.closest('.elementor-popup-modal, .jet-popup, .dialog-widget, .jet-popup-container');
98        if (parentPopup) {
99            parentPopup.appendChild(popup);
100        } else if (popup.parentNode !== document.body) {
101            document.body.appendChild(popup);
102        }
103
104        currentInput = input;
105        lastOpenTime = Date.now();
106
107        // Clear previous calendar and destroy instance
108        if (activeCalendars.has(popup)) {
109            var oldCalendar = activeCalendars.get(popup);
110            if (oldCalendar && typeof oldCalendar.destroy === 'function') {
111                oldCalendar.destroy();
112            }
113            activeCalendars.delete(popup);
114        }
115        popup.innerHTML = '';
116
117        // Parse existing value if any (format: YYYY/MM/DD)
118        var targetInput = input;
119        var selectedDate = new Date();
120        var existingValue = targetInput.value;
121        if (existingValue && typeof PersianElementorDateConverter !== 'undefined') {
122            var parts = existingValue.split('/');
123            if (parts.length === 3) {
124                var jy = parseInt(parts[0], 10);
125                var jm = parseInt(parts[1], 10);
126                var jd = parseInt(parts[2], 10);
127                var gregorian = PersianElementorDateConverter.jalaliToGregorian(jy, jm, jd);
128                if (gregorian[0] > 0) {
129                    selectedDate = new Date(gregorian[0], gregorian[1] - 1, gregorian[2]);
130                }
131            }
132        }
133
134        // Create calendar with closure for this specific input
135        var calendar = new PersianElementorCalendar(popup, {
136            selectedDate: selectedDate,
137            title: 'انتخاب تاریخ',
138            showCloseButton: true,
139            onDateSelect: function (dateInfo) {
140                var jalali = dateInfo.jalali;
141                var formatted = jalali.year + '/' +
142                    String(jalali.month).padStart(2, '0') + '/' +
143                    String(jalali.day).padStart(2, '0');
144
145                // Use the captured targetInput reference
146                targetInput.value = formatted;
147
148                // Trigger change event
149                var event = new Event('change', { bubbles: true });
150                targetInput.dispatchEvent(event);
151
152                hideCalendar();
153            },
154            onClose: function () {
155                hideCalendar();
156            }
157        });
158
159        activeCalendars.set(popup, calendar);
160
161        // Set default font to Vazirmatn
162        popup.style.fontFamily = 'VazirMatn, Vazir, Vazirmatn, VazirmatnVF, sans-serif';
163
164        // Prevent clicks inside popup from closing other popups
165        ['click', 'mousedown', 'mouseup', 'pointerdown', 'pointerup', 'touchstart', 'touchend'].forEach(function (event) {
166            popup.addEventListener(event, function (e) {
167                e.stopPropagation();
168            });
169        });
170
171        // Show popup
172        popup.style.display = 'block';
173        positionPopup(input);
174
175        // Mark input as active
176        input.setAttribute('data-calendar-open', 'true');
177    }
178
179    /**
180     * Hide calendar popup
181     */
182    function hideCalendar() {
183        if (calendarPopup) {
184            calendarPopup.style.display = 'none';
185            calendarPopup.innerHTML = '';
186        }
187        if (currentInput) {
188            currentInput.removeAttribute('data-calendar-open');
189            currentInput = null;
190        }
191    }
192
193    /**
194     * Initialize a single date field
195     */
196    function initPersianDateField(inputOrId) {
197        var input = typeof inputOrId === 'string' ?
198            document.getElementById(inputOrId) : inputOrId;
199
200        if (!input) return false;
201        if (input.getAttribute('data-persian-date-initialized')) return true;
202
203        input.setAttribute('data-persian-date-initialized', 'true');
204
205        // Only use click event, not focus
206        input.addEventListener('click', function (e) {
207            e.preventDefault();
208            e.stopPropagation();
209
210            // Toggle calendar
211            if (isCalendarOpen() && currentInput === input) {
212                hideCalendar();
213            } else {
214                showCalendar(input);
215            }
216        });
217
218        // Prevent focus from doing anything
219        input.addEventListener('focus', function (e) {
220            // Don't auto-open on focus, only on click
221        });
222
223        return true;
224    }
225
226    /**
227     * Initialize all date fields on page
228     */
229    function initAllDateFields() {
230        var fields = document.querySelectorAll('[data-persian-date]:not([data-persian-date-initialized])');
231        fields.forEach(function (field) {
232            initPersianDateField(field);
233        });
234    }
235
236    /**
237     * Close calendar when clicking outside
238     */
239    document.addEventListener('click', function (e) {
240        if (!isCalendarOpen()) return;
241
242        // Don't close if clicked within 100ms of opening (prevents race condition)
243        if (Date.now() - lastOpenTime < 100) return;
244
245        // Don't close if clicking inside calendar
246        if (calendarPopup.contains(e.target)) return;
247
248        // Don't close if clicking the input that opened it
249        if (currentInput && e.target === currentInput) return;
250
251        hideCalendar();
252    });
253
254    // Initialize when DOM is ready
255    if (document.readyState === 'loading') {
256        document.addEventListener('DOMContentLoaded', function () {
257            initAllDateFields();
258        });
259    } else {
260        initAllDateFields();
261    }
262
263    // MutationObserver for dynamic content
264    var observer = new MutationObserver(function (mutations) {
265        var shouldReinit = false;
266
267        mutations.forEach(function (mutation) {
268            if (mutation.type === 'childList') {
269                mutation.addedNodes.forEach(function (node) {
270                    if (node.nodeType === Node.ELEMENT_NODE) {
271                        if (node.matches && node.matches('[data-persian-date]')) {
272                            shouldReinit = true;
273                        } else if (node.querySelector && node.querySelector('[data-persian-date]')) {
274                            shouldReinit = true;
275                        }
276                    }
277                });
278            }
279        });
280
281        if (shouldReinit) {
282            initAllDateFields();
283        }
284    });
285
286    observer.observe(document.body, {
287        childList: true,
288        subtree: true
289    });
290
291    // Expose functions globally
292    window.initPersianDateField = initPersianDateField;
293    window.initAllPersianDateFields = initAllDateFields;
294    window.hidePersianCalendar = hideCalendar;
295
296    // Hook into Elementor and JetPopup events to re-initialize cloned inputs inside popups
297    if (typeof window !== 'undefined' && window.jQuery) {
298        window.jQuery(document).on('elementor/popup/show', function(event, id, instance) {
299            if (instance && instance.$element) {
300                instance.$element.find('[data-persian-date]').removeAttr('data-persian-date-initialized');
301                initAllDateFields();
302            }
303        });
304        window.jQuery(document).on('jet-popup/show-event', function(event, $popup) {
305            if ($popup) {
306                $popup.find('[data-persian-date]').removeAttr('data-persian-date-initialized');
307                initAllDateFields();
308            }
309        });
310    }
311
312})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.