1+ /**
2+ * Quick Navigation Media Live Search
3+ * Erweitert das bestehende Mediapool-Suchfeld um Live-Suche
4+ */
5+
6+ $ ( document ) . on ( 'rex:ready' , function ( ) {
7+ initQuickNavigationMediaLiveSearch ( ) ;
8+ } ) ;
9+
10+ function initQuickNavigationMediaLiveSearch ( ) {
11+ // Prüfen ob Live-Search vom User aktiviert ist
12+ if ( typeof rex !== 'undefined' && rex . QUICKNAV_MEDIA_LIVESEARCH_ENABLED === false ) {
13+ return ;
14+ }
15+
16+ // Prüfen ob wir im Mediapool sind
17+ var isMediapool = $ ( 'body' ) . hasClass ( 'rex-page-mediapool' ) ||
18+ $ ( '[data-page="mediapool"]' ) . length > 0 ||
19+ window . location . href . indexOf ( 'page=mediapool' ) !== - 1 ;
20+
21+ if ( ! isMediapool ) {
22+ return ;
23+ }
24+
25+ var searchInput = $ ( '#be_search-media-name' ) ;
26+ var searchResults = null ;
27+ var searchTimer = null ;
28+ var currentRequest = null ;
29+
30+ if ( searchInput . length === 0 ) {
31+ return ;
32+ }
33+
34+ // Container für Live-Suche Ergebnisse relativ zum bestehenden Suchfeld erstellen
35+ var resultsContainer = $ ( '<div class="qn-media-live-search-results"></div>' ) ;
36+ var searchForm = searchInput . closest ( '.form-group, .input-group, form' ) ;
37+ searchForm . css ( 'position' , 'relative' ) ;
38+ searchForm . append ( resultsContainer ) ;
39+
40+ // Event Handler für Live-Suche (zusätzlich zur normalen Suchfunktion)
41+ searchInput . on ( 'input.quicknav' , function ( ) {
42+ var searchTerm = $ ( this ) . val ( ) . trim ( ) ;
43+
44+ // Timer zurücksetzen
45+ if ( searchTimer ) {
46+ clearTimeout ( searchTimer ) ;
47+ }
48+
49+ // Laufende Requests abbrechen
50+ if ( currentRequest ) {
51+ currentRequest . abort ( ) ;
52+ currentRequest = null ;
53+ }
54+
55+ if ( searchTerm . length === 0 ) {
56+ resultsContainer . hide ( ) ;
57+ return ;
58+ }
59+
60+ if ( searchTerm . length < 2 ) {
61+ return ; // Erst ab 2 Zeichen Live-Suche
62+ }
63+
64+ // Kurz warten vor der Suche (Debouncing)
65+ searchTimer = setTimeout ( function ( ) {
66+ performQuickNavMediaSearch ( searchTerm , resultsContainer ) ;
67+ } , 300 ) ;
68+ } ) ;
69+
70+ // Klick außerhalb schließt die Ergebnisse
71+ $ ( document ) . on ( 'click.quicknav' , function ( e ) {
72+ if ( ! $ ( e . target ) . closest ( '.qn-media-live-search-results, #be_search-media-name' ) . length ) {
73+ resultsContainer . hide ( ) ;
74+ }
75+ } ) ;
76+
77+ // ESC schließt die Ergebnisse
78+ searchInput . on ( 'keydown.quicknav' , function ( e ) {
79+ if ( e . keyCode === 27 ) { // ESC
80+ resultsContainer . hide ( ) ;
81+ }
82+ } ) ;
83+
84+ function performQuickNavMediaSearch ( searchTerm , container ) {
85+ // Loading anzeigen
86+ container . html ( '<div class="qn-live-search-loading"><i class="fa fa-spinner fa-spin"></i> Suche läuft...</div>' )
87+ . show ( ) ;
88+
89+ // Parameter aus der aktuellen Seite holen
90+ var categoryId = $ ( '#rex_file_category' ) . val ( ) || 0 ;
91+ var openerInputField = $ ( 'input[name="opener_input_field"]' ) . val ( ) || '' ;
92+ var types = $ ( 'input[name="args[types]"]' ) . val ( ) || '' ;
93+
94+ // AJAX Request zur Quick Navigation API
95+ currentRequest = $ . ajax ( {
96+ url : window . location . pathname + window . location . search ,
97+ method : 'GET' ,
98+ data : {
99+ 'rex-api-call' : 'quicknavigation_media_search' ,
100+ 'term' : searchTerm ,
101+ 'category_id' : categoryId ,
102+ 'opener_input_field' : openerInputField ,
103+ 'types' : types
104+ } ,
105+ dataType : 'json' ,
106+ success : function ( response ) {
107+ currentRequest = null ;
108+
109+ if ( response . success ) {
110+ displayQuickNavSearchResults ( response . results , container ) ;
111+ } else {
112+ container . html ( '<div class="qn-live-search-no-results">Fehler: ' + ( response . error || 'Unbekannter Fehler' ) + '</div>' ) ;
113+ }
114+ } ,
115+ error : function ( xhr , status , error ) {
116+ currentRequest = null ;
117+
118+ if ( status !== 'abort' ) {
119+ container . html ( '<div class="qn-live-search-no-results">Fehler beim Laden der Suchergebnisse</div>' ) ;
120+ }
121+ }
122+ } ) ;
123+ }
124+ }
125+
126+ function displayQuickNavSearchResults ( results , container ) {
127+ if ( results . length === 0 ) {
128+ container . html ( '<div class="qn-live-search-no-results">Keine Ergebnisse gefunden</div>' ) ;
129+ container . show ( ) ;
130+ return ;
131+ }
132+
133+ var html = '' ;
134+
135+ $ . each ( results , function ( index , item ) {
136+ var thumbnail = '' ;
137+
138+ if ( item . thumbnail . type === 'image' ) {
139+ thumbnail = '<img src="' + item . thumbnail . src + '" alt="' + item . thumbnail . alt + '" loading="lazy">' ;
140+ } else if ( item . thumbnail . type === 'icon' ) {
141+ thumbnail = '<i class="' + item . thumbnail . icon + '" title="' + item . thumbnail . title + '"></i>' ;
142+ } else if ( item . thumbnail . type === 'error' ) {
143+ thumbnail = '<i class="' + item . thumbnail . icon + '" title="' + item . thumbnail . title + '" style="color: #dc3545;"></i>' ;
144+ }
145+
146+ var detailUrl = '' ;
147+ if ( item . actions . edit ) {
148+ detailUrl = item . actions . edit . url ;
149+ }
150+
151+ if ( item . actions . select ) {
152+ // Widget-Modus: Buttons untereinander anzeigen
153+ html += '<div class="qn-live-search-item">' +
154+ '<div class="qn-live-search-item-content">' +
155+ '<div class="qn-live-search-thumb">' + thumbnail + '</div>' +
156+ '<div class="qn-live-search-info">' +
157+ '<div class="qn-live-search-title">' + item . title + '</div>' +
158+ '<div class="qn-live-search-filename">' + item . filename + '</div>' +
159+ '<div class="qn-live-search-meta">' + item . size + ' • ' + item . updatedate + '</div>' +
160+ '</div>' +
161+ '</div>' +
162+ '<div class="qn-live-search-actions">' ;
163+
164+ // Übernehmen-Button
165+ html += '<button type="button" class="btn btn-xs btn-select" onclick="' ;
166+
167+ if ( item . actions . select . type === 'media' ) {
168+ html += 'selectMedia(\'' + item . actions . select . filename + '\', \'' + item . actions . select . title + '\'); $(this).closest(\'.qn-media-live-search-results\').hide(); return false;' ;
169+ } else if ( item . actions . select . type === 'medialist' ) {
170+ html += 'selectMedialist(\'' + item . actions . select . filename + '\', this); $(this).closest(\'.qn-media-live-search-results\').hide(); return false;' ;
171+ }
172+
173+ html += '">' + item . actions . select . label + '</button>' ;
174+
175+ // Details-Button
176+ if ( detailUrl ) {
177+ html += '<a href="' + detailUrl + '" class="btn btn-xs btn-default">Details</a>' ;
178+ }
179+
180+ html += '</div></div>' ;
181+
182+ } else if ( detailUrl ) {
183+ // Normaler Modus: Komplettes Element als Link zur Detailansicht
184+ html += '<a href="' + detailUrl + '" class="qn-live-search-item" style="color: inherit; text-decoration: none;">' +
185+ '<div class="qn-live-search-item-content">' +
186+ '<div class="qn-live-search-thumb">' + thumbnail + '</div>' +
187+ '<div class="qn-live-search-info">' +
188+ '<div class="qn-live-search-title">' + item . title + '</div>' +
189+ '<div class="qn-live-search-filename">' + item . filename + '</div>' +
190+ '<div class="qn-live-search-meta">' + item . size + ' • ' + item . updatedate + '</div>' +
191+ '</div>' +
192+ '</div>' +
193+ '</a>' ;
194+ } else {
195+ // Fallback ohne Link
196+ html += '<div class="qn-live-search-item">' +
197+ '<div class="qn-live-search-item-content">' +
198+ '<div class="qn-live-search-thumb">' + thumbnail + '</div>' +
199+ '<div class="qn-live-search-info">' +
200+ '<div class="qn-live-search-title">' + item . title + '</div>' +
201+ '<div class="qn-live-search-filename">' + item . filename + '</div>' +
202+ '<div class="qn-live-search-meta">' + item . size + ' • ' + item . updatedate + '</div>' +
203+ '</div>' +
204+ '</div>' +
205+ '</div>' ;
206+ }
207+ } ) ;
208+
209+ container . html ( html ) . show ( ) ;
210+ }
0 commit comments