1+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
2+ function setLanguageDirection ( lang ) {
3+ const isEnglish = lang . toLowerCase ( ) . startsWith ( 'en' ) ;
4+ const direction = isEnglish ? 'ltr' : 'rtl' ;
5+ document . documentElement . setAttribute ( 'dir' , direction ) ;
6+ localStorage . setItem ( 'preferred_direction' , direction ) ;
7+ }
8+
9+ function initializeDirection ( ) {
10+ const userLang = navigator . language || navigator . userLanguage || 'en' ;
11+ setLanguageDirection ( userLang ) ;
12+ }
13+
14+ function restoreDirectionPreference ( ) {
15+ const savedDirection = localStorage . getItem ( 'preferred_direction' ) ;
16+ if ( savedDirection ) {
17+ document . documentElement . setAttribute ( 'dir' , savedDirection ) ;
18+ } else {
19+ initializeDirection ( ) ;
20+ }
21+ }
22+
23+ function handleResponsiveNav ( ) {
24+ const navLinks = document . querySelector ( '.nav-links' ) ;
25+ if ( ! navLinks ) return ;
26+ if ( window . innerWidth <= 768 ) {
27+ navLinks . classList . add ( 'mobile-ready' ) ;
28+ } else {
29+ navLinks . classList . remove ( 'mobile-ready' , 'active' ) ;
30+ }
31+ }
32+
33+ function throttle ( func , limit ) {
34+ let inThrottle ;
35+ return function ( ...args ) {
36+ if ( ! inThrottle ) {
37+ func . apply ( this , args ) ;
38+ inThrottle = true ;
39+ setTimeout ( ( ) => ( inThrottle = false ) , limit ) ;
40+ }
41+ } ;
42+ }
43+
44+ restoreDirectionPreference ( ) ;
45+
46+ const toggle = document . querySelector ( '.nav-toggle' ) ;
47+ const navLinks = document . querySelector ( '.nav-links' ) ;
48+ const navbar = document . querySelector ( '.navbar' ) ;
49+
50+ if ( toggle && navLinks ) {
51+ toggle . addEventListener ( 'click' , ( e ) => {
52+ e . stopPropagation ( ) ;
53+ navLinks . classList . toggle ( 'active' ) ;
54+ const isExpanded = navLinks . classList . contains ( 'active' ) ;
55+ toggle . setAttribute ( 'aria-expanded' , isExpanded ) ;
56+ } ) ;
57+
58+ document . addEventListener ( 'click' , ( e ) => {
59+ if ( ! navLinks . contains ( e . target ) && ! toggle . contains ( e . target ) ) {
60+ navLinks . classList . remove ( 'active' ) ;
61+ toggle . setAttribute ( 'aria-expanded' , 'false' ) ;
62+ }
63+ } ) ;
64+
65+ document . addEventListener ( 'keydown' , ( e ) => {
66+ if ( e . key === 'Escape' && navLinks . classList . contains ( 'active' ) ) {
67+ navLinks . classList . remove ( 'active' ) ;
68+ toggle . setAttribute ( 'aria-expanded' , 'false' ) ;
69+ toggle . focus ( ) ;
70+ }
71+ } ) ;
72+
73+ navLinks . addEventListener ( 'click' , ( e ) => {
74+ const link = e . target . closest ( 'a' ) ;
75+ if ( link ) {
76+ const linkText = link . textContent . trim ( ) . toLowerCase ( ) ;
77+ if ( linkText . includes ( 'english' ) ) {
78+ setLanguageDirection ( 'en' ) ;
79+ } else if ( linkText . includes ( 'العربية' ) ) {
80+ setLanguageDirection ( 'ar' ) ;
81+ }
82+ if ( window . innerWidth <= 768 ) {
83+ navLinks . classList . remove ( 'active' ) ;
84+ toggle . setAttribute ( 'aria-expanded' , 'false' ) ;
85+ }
86+ }
87+ } ) ;
88+ }
89+
90+ if ( navbar ) {
91+ const handleScroll = throttle ( ( ) => {
92+ const shouldShowScrolled = window . scrollY > 50 ;
93+ navbar . classList . toggle ( 'scrolled' , shouldShowScrolled ) ;
94+ if ( shouldShowScrolled ) {
95+ navbar . style . boxShadow = '0 4px 30px rgba(0, 0, 0, 0.3)' ;
96+ } else {
97+ navbar . style . boxShadow = '0 2px 10px rgba(0, 0, 0, 0.1)' ;
98+ }
99+ } , 10 ) ;
100+
101+ window . addEventListener ( 'scroll' , handleScroll , { passive : true } ) ;
102+ }
103+
104+ const observerOptions = {
105+ threshold : 0.1 ,
106+ rootMargin : '0px 0px -50px 0px'
107+ } ;
108+
109+ const animationObserver = new IntersectionObserver ( ( entries ) => {
110+ entries . forEach ( ( entry ) => {
111+ if ( entry . isIntersecting ) {
112+ entry . target . classList . add ( 'visible' ) ;
113+ const siblings = entry . target . parentElement ?. children ;
114+ if ( siblings ) {
115+ Array . from ( siblings ) . forEach ( ( sibling , index ) => {
116+ if ( sibling . classList . contains ( 'fade-up' ) ) {
117+ sibling . style . transitionDelay = `${ index * 0.1 } s` ;
118+ }
119+ } ) ;
120+ }
121+ animationObserver . unobserve ( entry . target ) ;
122+ }
123+ } ) ;
124+ } , observerOptions ) ;
125+
126+ const animatableElements = document . querySelectorAll (
127+ '.fade-up, .card, .lang-item, .review-card, .feature-category, .option-item, .support-card'
128+ ) ;
129+ animatableElements . forEach ( ( el ) => animationObserver . observe ( el ) ) ;
130+
131+ document . querySelectorAll ( 'a[href^="#"]' ) . forEach ( ( link ) => {
132+ link . addEventListener ( 'click' , function ( e ) {
133+ e . preventDefault ( ) ;
134+ const targetId = this . getAttribute ( 'href' ) ;
135+ if ( targetId === '#' ) return ;
136+ const targetElement = document . querySelector ( targetId ) ;
137+ if ( targetElement ) {
138+ const navbarHeight = navbar ? navbar . offsetHeight : 70 ;
139+ const targetPosition = targetElement . getBoundingClientRect ( ) . top + window . pageYOffset - navbarHeight ;
140+ window . scrollTo ( {
141+ top : targetPosition ,
142+ behavior : 'smooth'
143+ } ) ;
144+ history . pushState ( null , null , targetId ) ;
145+ if ( navLinks ) {
146+ navLinks . classList . remove ( 'active' ) ;
147+ if ( toggle ) toggle . setAttribute ( 'aria-expanded' , 'false' ) ;
148+ }
149+ }
150+ } ) ;
151+ } ) ;
152+
153+ const themeToggle = document . querySelector ( '.theme-toggle' ) ;
154+ if ( themeToggle ) {
155+ const applyTheme = ( theme ) => {
156+ if ( theme === 'dark' ) {
157+ document . body . classList . add ( 'dark-mode' ) ;
158+ themeToggle . innerHTML = '<i class="fas fa-sun"></i>' ;
159+ themeToggle . setAttribute ( 'aria-label' , 'تفعيل الوضع النهاري' ) ;
160+ } else {
161+ document . body . classList . remove ( 'dark-mode' ) ;
162+ themeToggle . innerHTML = '<i class="fas fa-moon"></i>' ;
163+ themeToggle . setAttribute ( 'aria-label' , 'تفعيل الوضع الليلي' ) ;
164+ }
165+ } ;
166+
167+ const savedTheme = localStorage . getItem ( 'theme' ) ;
168+ if ( savedTheme ) {
169+ applyTheme ( savedTheme ) ;
170+ } else {
171+ const prefersDark = window . matchMedia ( '(prefers-color-scheme: dark)' ) . matches ;
172+ applyTheme ( prefersDark ? 'dark' : 'light' ) ;
173+ }
174+
175+ themeToggle . addEventListener ( 'click' , ( ) => {
176+ const isDarkMode = document . body . classList . contains ( 'dark-mode' ) ;
177+ const newTheme = isDarkMode ? 'light' : 'dark' ;
178+ applyTheme ( newTheme ) ;
179+ localStorage . setItem ( 'theme' , newTheme ) ;
180+ } ) ;
181+
182+ window . matchMedia ( '(prefers-color-scheme: dark)' ) . addEventListener ( 'change' , ( e ) => {
183+ if ( ! localStorage . getItem ( 'theme' ) ) {
184+ applyTheme ( e . matches ? 'dark' : 'light' ) ;
185+ }
186+ } ) ;
187+ }
188+
189+ handleResponsiveNav ( ) ;
190+
191+ document . querySelectorAll ( '.btn-3d, button, .nav-toggle' ) . forEach ( ( btn ) => {
192+ btn . addEventListener ( 'touchstart' , function ( e ) {
193+ e . preventDefault ( ) ;
194+ this . click ( ) ;
195+ } , { passive : false } ) ;
196+ } ) ;
197+
198+ document . addEventListener ( 'keydown' , ( e ) => {
199+ if ( e . altKey && ! e . ctrlKey && ! e . metaKey ) {
200+ const sections = document . querySelectorAll ( 'section[id]' ) ;
201+ const sectionIndex = parseInt ( e . key ) - 1 ;
202+ if ( sectionIndex >= 0 && sectionIndex < sections . length ) {
203+ e . preventDefault ( ) ;
204+ sections [ sectionIndex ] . scrollIntoView ( { behavior : 'smooth' } ) ;
205+ }
206+ }
207+ } ) ;
208+
209+ const lazyImages = document . querySelectorAll ( 'img[loading="lazy"]' ) ;
210+ if ( 'loading' in HTMLImageElement . prototype ) {
211+ lazyImages . forEach ( ( img ) => {
212+ img . src = img . dataset . src ;
213+ } ) ;
214+ } else {
215+ const lazyImageObserver = new IntersectionObserver ( ( entries ) => {
216+ entries . forEach ( ( entry ) => {
217+ if ( entry . isIntersecting ) {
218+ const img = entry . target ;
219+ img . src = img . dataset . src ;
220+ img . classList . add ( 'loaded' ) ;
221+ lazyImageObserver . unobserve ( img ) ;
222+ }
223+ } ) ;
224+ } ) ;
225+ lazyImages . forEach ( ( img ) => lazyImageObserver . observe ( img ) ) ;
226+ }
227+
228+ if ( window . performance && window . performance . getEntriesByType ) {
229+ window . addEventListener ( 'load' , ( ) => {
230+ const navigationEntry = performance . getEntriesByType ( 'navigation' ) [ 0 ] ;
231+ if ( navigationEntry ) {
232+ console . log ( `وقت تحميل الصفحة: ${ navigationEntry . loadEventEnd - navigationEntry . startTime } ms` ) ;
233+ }
234+ } ) ;
235+ }
236+
237+ window . addEventListener ( 'error' , ( e ) => {
238+ if ( e . target . tagName === 'LINK' || e . target . tagName === 'SCRIPT' ) {
239+ console . warn ( `فشل تحميل المورد: ${ e . target . src || e . target . href } ` ) ;
240+ }
241+ } , true ) ;
242+
243+ } ) ;
244+
245+ window . addEventListener ( 'resize' , throttle ( handleResponsiveNav , 200 ) ) ;
246+
247+ window . addEventListener ( 'languagechange' , ( ) => {
248+ initializeDirection ( ) ;
249+ } ) ;
250+
251+ if ( typeof window !== 'undefined' ) {
252+ window . PyToExe = {
253+ setDirection : setLanguageDirection ,
254+ toggleTheme : ( ) => {
255+ const event = new Event ( 'click' ) ;
256+ document . querySelector ( '.theme-toggle' ) ?. dispatchEvent ( event ) ;
257+ } ,
258+ scrollToSection : ( sectionId ) => {
259+ const section = document . querySelector ( sectionId ) ;
260+ if ( section ) {
261+ section . scrollIntoView ( { behavior : 'smooth' } ) ;
262+ }
263+ } ,
264+ resetPreferences : ( ) => {
265+ localStorage . clear ( ) ;
266+ location . reload ( ) ;
267+ }
268+ } ;
269+ }
270+
271+ console . log ( 'From Python To Executable' ) ;
0 commit comments